Windows7下驱动开发与调试体系构建——2.R3与R0的通信示例
在阅读本节前,建议先阅读《Windows内核安全与驱动开发》第五章内容,并自行了解相关背景知识。
R0部分
创建项目,打开:项目属性->链接器->输入->附加依赖项(点开后选择【编辑】),添加:
%(AdditionalDependencies)
$(DDK_LIB_PATH)\wdmsec.lib
参考代码:
#include <ntifs.h>
#include <wdmsec.h> static PDEVICE_OBJECT g_cdo = NULL; const GUID CWK_GUID_CLASS_MYCDO =
{ 0x17a0d1e0L, 0x3249, 0x12e1, {0x92,0x16, 0x45, 0x1a, 0x21, 0x30, 0x29, 0x06} }; #define CWK_CDO_SYB_NAME L"\\??\\slbkcdo_3948d33e" // 从应用层给驱动发送一个字符串。
#define CWK_DVC_SEND_STR \
(ULONG)CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
0x911,METHOD_BUFFERED, \
FILE_WRITE_DATA) // 从驱动读取一个字符串
#define CWK_DVC_RECV_STR \
(ULONG)CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
0x912,METHOD_BUFFERED, \
FILE_READ_DATA) // 定义一个链表用来保存字符串
#define CWK_STR_LEN_MAX 512
typedef struct {
LIST_ENTRY list_entry;
char buf[CWK_STR_LEN_MAX];
} CWK_STR_NODE; // 还必须有一把自旋锁来保证链表操作的安全性
KSPIN_LOCK g_cwk_lock;
// 一个事件来标识是否有字符串可以取
KEVENT g_cwk_event;
// 必须有个链表头
LIST_ENTRY g_cwk_str_list; #define MEM_TAG 'cwkr' // 分配内存并初始化一个链表节点
CWK_STR_NODE *cwkMallocStrNode()
{
CWK_STR_NODE *ret = ExAllocatePoolWithTag(
NonPagedPool, sizeof(CWK_STR_NODE), MEM_TAG);
if (ret == NULL)
return NULL;
return ret;
} void cwkUnload(PDRIVER_OBJECT driver)
{
DbgPrint("\r\n准备卸载\r\n");
UNICODE_STRING cdo_syb = RTL_CONSTANT_STRING(CWK_CDO_SYB_NAME);
CWK_STR_NODE *str_node;
//ASSERT(g_cdo != NULL);//bug崩溃?
DbgPrint("准备删除符号链接\r\n");
IoDeleteSymbolicLink(&cdo_syb);
DbgPrint("准备删除驱动对象\r\n");
if(g_cdo!=NULL)IoDeleteDevice(g_cdo);
else {
DbgPrint("\r\n发生严重错误:g_cdo==NULL\r\n");
} DbgPrint("\r\n准备释放内存\r\n");
// 负责的编程态度:释放分配过的所有内核内存。
while (TRUE)
{
str_node = (CWK_STR_NODE *)ExInterlockedRemoveHeadList(
&g_cwk_str_list, &g_cwk_lock);
// str_node = RemoveHeadList(&g_cwk_str_list);
if (str_node != NULL)
ExFreePool(str_node);
else
break;
}; DbgPrint("\r\n卸载完成\r\n");
} NTSTATUS cwkDispatch(
IN PDEVICE_OBJECT dev,
IN PIRP irp)
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status = STATUS_SUCCESS;
ULONG ret_len = 0;
while (1)
{
if (irpsp->MajorFunction == IRP_MJ_CREATE || irpsp->MajorFunction == IRP_MJ_CLOSE)
{
break;
} if (irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
// 处理DeviceIoControl。
PVOID buffer = irp->AssociatedIrp.SystemBuffer;
ULONG inlen = irpsp->Parameters.DeviceIoControl.InputBufferLength;
ULONG outlen = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
ULONG len;
CWK_STR_NODE *str_node;
switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
{
case CWK_DVC_SEND_STR: ASSERT(buffer != NULL);
ASSERT(outlen == 0);
if (inlen > CWK_STR_LEN_MAX)
{
status = STATUS_INVALID_PARAMETER;
break;
} DbgPrint("\r\n接收到来自r3的信息:%s\r\n", (char *)buffer);
str_node = cwkMallocStrNode();
if (str_node == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} //char* tmpBuffer= RTL_CONSTANT_STRING(L"QWQ");//字符缓冲
//strncpy(tmpBuffer, (char *)buffer, CWK_STR_LEN_MAX); strncpy(str_node->buf, (char *)buffer, CWK_STR_LEN_MAX); strncpy(str_node->buf, (char *)buffer, CWK_STR_LEN_MAX); char tmp1[512] = "Hello,Ring3!";
strncpy(str_node->buf, tmp1, CWK_STR_LEN_MAX);
// 插入到链表末尾。用锁来保证安全性。
ExInterlockedInsertTailList(&g_cwk_str_list, (PLIST_ENTRY)str_node, &g_cwk_lock);
// InsertTailList(&g_cwk_str_list, (PLIST_ENTRY)str_node);
// 打印
// DbgPrint((char *)buffer);
// 那么现在就可以认为这个请求已经成功。因为刚刚已经插入了一
// 个,那么可以设置事件来表明队列中已经有元素了。
KeSetEvent(&g_cwk_event, 0, FALSE);//设为TRUE就是等死
//DbgPrint("\r\n信息处理完成!\r\n");
break;
case CWK_DVC_RECV_STR: //ASSERT(buffer != NULL);
//ASSERT(inlen == 0);
if (outlen < CWK_STR_LEN_MAX)
{
DbgPrint("\r\n长度太短..\r\n");
status = STATUS_INVALID_PARAMETER;
break;
}
while (1)
{
// 插入到链表末尾。用锁来保证安全性。
str_node = (CWK_STR_NODE *)ExInterlockedRemoveHeadList(&g_cwk_str_list, &g_cwk_lock);
// str_node = RemoveHeadList(&g_cwk_str_list);
if (str_node != NULL)
{
// 这种情况下,取得了字符串。那就拷贝到输出缓冲中。然后
// 整个请求就返回了成功。
strncpy((char *)buffer, str_node->buf, CWK_STR_LEN_MAX);
ret_len = strnlen(str_node->buf, CWK_STR_LEN_MAX) + 1;
DbgPrint("\r\n准备发送信息,%s\r\n", str_node->buf);
ExFreePool(str_node);
break;
}
else
{
// 对于合法的要求,在缓冲链表为空的情况下,等待事件进行
// 阻塞。也就是说,如果缓冲区中没有字符串,就停下来等待
// 。这样应用程序也会被阻塞住,DeviceIoControl是不会返回
// 的。但是一旦有就会返回。等于驱动“主动”通知了应用。
KeWaitForSingleObject(&g_cwk_event, Executive, KernelMode, 0, 0);
}
}
break;
default:
// 到这里的请求都是不接受的请求。未知的请求一律返回非法参数错误。
status = STATUS_INVALID_PARAMETER; DbgPrint("\r\n出现未知请求!!\r\n");
break;
}
}
break;
}
// 返回结果
irp->IoStatus.Information = ret_len;
irp->IoStatus.Status = status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
//DbgPrint("\r\n成功返回结果!\r\n");
return status;
} NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
NTSTATUS status;
ULONG i;
UCHAR mem[256] = { 0 }; // 生成一个控制设备。然后生成符号链接。
UNICODE_STRING sddl = RTL_CONSTANT_STRING(L"D:P(A;;GA;;;WD)");
UNICODE_STRING cdo_name = RTL_CONSTANT_STRING(L"\\Device\\cwk_3948d33e");
UNICODE_STRING cdo_syb = RTL_CONSTANT_STRING(CWK_CDO_SYB_NAME); //KdBreakPoint();
// 生成一个控制设备对象。
status = IoCreateDeviceSecure(
driver,
0, &cdo_name,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE, &sddl,
(LPCGUID)&CWK_GUID_CLASS_MYCDO,
&g_cdo);
if (!NT_SUCCESS(status))
return status; // 生成符号链接.
IoDeleteSymbolicLink(&cdo_syb);
status = IoCreateSymbolicLink(&cdo_syb, &cdo_name);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(g_cdo);
return status;
} // 初始化事件、锁、链表头。
KeInitializeEvent(&g_cwk_event, SynchronizationEvent, TRUE);
KeInitializeSpinLock(&g_cwk_lock);
InitializeListHead(&g_cwk_str_list); // 所有的分发函数都设置成一样的。
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
driver->MajorFunction[i] = cwkDispatch;
}
//DbgPrint("test1\r\n"); // 支持动态卸载。
driver->DriverUnload = cwkUnload;
//DbgPrint("test2\r\n");
// 清除控制设备的初始化标记。
//g_cdo->Flags &= ~DO_DEVICE_INITIALIZING;
//DbgPrint("test3\r\n");
return STATUS_SUCCESS;
}
R3部分
参考代码:
#include <stdio.h>
#include <tchar.h>
#include <windows.h> const char* CWK_DEV_SYM = "\\\\.\\slbkcdo_3948d33e"; // 从应用层给驱动发送一个字符串。
#define CWK_DVC_SEND_STR \
(ULONG)CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
0x911,METHOD_BUFFERED, \
FILE_WRITE_DATA) // 从驱动读取一个字符串
#define CWK_DVC_RECV_STR \
(ULONG)CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
0x912,METHOD_BUFFERED, \
FILE_READ_DATA) int _tmain(int argc, _TCHAR* argv[])
{
HANDLE device = NULL;
ULONG ret_len;
int ret = 0;
char tmp[] = "Hello driver, this is a message from app.\r\n";
char* msg = tmp;
char tst_msg[1024] = { 0 }; // 打开设备.每次要操作驱动的时候,先以此为例子打开设备
device=CreateFile(CWK_DEV_SYM,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,0);
if (device == INVALID_HANDLE_VALUE)
{
printf("coworker demo: Open device failed.\r\n"); int VAKb;
scanf_s("%d", &VAKb);
return -1;
}
else
printf("coworker demo: Open device successfully.\r\n");
/////// // 这里开始,其实是对驱动的一系列测试。分配3个字符串:
// 1.长度为0.应该可以正常输入。
// 2.长度为511字节,应该可以正常输入。
// 3.长度为512字节,应该返回失败。
// 4.长度为1024字节的字符串,但声明缓冲区长度为128,应该返回失败。
// 5.第一次读取,应该读出msg的内容。
// 5.第一次读取,应该读出长度为511字节的字符串。
// 6.第二次读取,应该读出长度为0的字符串。
do {
if (!DeviceIoControl(device, CWK_DVC_SEND_STR, msg, strlen(msg) + 1, NULL, 0, &ret_len, 0))
{
printf("coworker demo: Send message failed.\r\n");
ret = -2;
}
else
printf("发送信息:%s\n", msg); if (DeviceIoControl(device, CWK_DVC_RECV_STR, NULL, 0, tst_msg, 1024, &ret_len, 0) == 0)
{
ret = -6;
break;
}
else
{
printf("收到的信息:%s\r\n",tst_msg);
}
} while (0); ///////
CloseHandle(device);
int VAKb;
scanf_s("%d", &VAKb);
return ret;
}
虚拟机内可能无法运行。解决方法:选择静态编译。项目属性-配置属性-C/C+±代码生成-运行库-多线程调试(/MTd)。
测试图例:
Windows7下驱动开发与调试体系构建——2.R3与R0的通信示例的更多相关文章
- Windows7下驱动开发与调试体系构建——0.概述
本文集内容为windows7x64下驱动开发与调试体系构建,内容目录如下: 1.驱动开发的环境准备 2.R3与R0的通信示例 3.自建调试体系概述 4.在x64下使用汇编代码 5.实战反调试标记位(N ...
- Windows7下驱动开发与调试体系构建——3.调试体系概述
目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 调试体系概述 0.什么是自建调试体系? 就是复写windows的调试api,使得调试 ...
- Windows7下驱动开发与调试体系构建——1.驱动开发的环境准备
目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 系统基础环境 开发环境 win7下开发驱动需要安装vs,这里使用2017. 安装vs ...
- Windows7下驱动开发与调试体系构建——5.实战反调试标记位(NtGlobalFlag)
目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html <加密与解密>P670中,介绍了检查程序是否被调试的第二种方法:查看进程 ...
- Windows7下驱动开发与调试体系构建——4.在x64下使用汇编代码(x86下的_asm)
目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html asm文件设置 在vs x64中无法使用_asm关键字,需要使用.asm文件. 按第 ...
- 2013-6-2 [转载自CSDN]如何入门Windows系统下驱动开发
[序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文为主,这样让很多驱动初学者很头疼.本人从事驱动开发时间不长也不短,大概 ...
- 如何正确入门Windows系统下驱动开发领域?
[作者]猪头三个人网站 :http://www.x86asm.com/ [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文 ...
- 驱动开发 - WDK 调试及 SVN 环境搭建
由于从公司辞职了,所以以前在公司里搭建的驱动开发环境也就 Game Over 了, 同样由于那环境是很久以前搭建的,自己也有很多记不清楚的地方了, 而且其中还是有很多需要注意的地方的,所以在这里顺便做 ...
- Windows7下QT5开发环境搭建 分类: QT开发 2015-03-09 23:44 65人阅读 评论(0) 收藏
Windows7下QT开法环境常见搭配方法有两种. 第一种是:QT Creator+QT SDK: 第二种是:VS+qt-vs-addin+QT SDK: 以上两种均可,所需文件见QT社区,QT下载地 ...
随机推荐
- 微服务性能分析|Pyroscope 在 Rainbond 上的实践分享
随着微服务体系在生产环境落地,也会伴随着一些问题出现,比如流量过大造成某个微服务应用程序的性能瓶颈.CPU利用率高.或内存泄漏等问题.要找到问题的根本原因,我们通常都会通过日志.进程再结合代码去判断根 ...
- ES6 Promise详解
前言 本文主要是对Promise本身的用法做一个全面解析而非它的原理实现,如果你对Promise的用法还不是很熟悉或者想加深你对Promise的理解,我相信这篇文章一定会帮到你. 首先让我们先了解一下 ...
- Linux操作系统学习(运维必会)
Linux一切皆文件,最高权限的账户root. 1.开机登录 开机会启动很多进程,在Windows上叫"服务"(service),在Linux上叫做"守护进程" ...
- JavaScript的入门
书写的三种方式 1. 书写在script标签里面(一般会用到的) 2.书写在js文件里面(推荐)定义一个js文件(xxx.js) 3. 书写对应的事件属性里面(比较少用) 初体验 1. 目前js的代码 ...
- PerfView专题 (第十二篇):对 C# 下的 SDK 类库进行监控(大结局)
一:背景 本篇是我们系列文章的最后一篇,前面的文章中大多是在 CLR Runtime 以及 OS 层面进行监控来发现各种可疑的程序问题,除了这两个层面,其实我们还可以对 SDK 中一些类进行洞察,比如 ...
- Sentinel控制台1.8.3修改源码,修改配置后推送到Nacos
目录 1. 接着上一篇 2. 思路 3. 下载Sentinel源码 4. 看Gateway里面读取的配置信息 5. 修改Sentinel控制台源码 6. 熔断规则测试 7. 限流规则测试 8. 打包使 ...
- wsl创建证书让chrome浏览器识别
我在wsl里面测试http2协议的时候,在wsl用openssl创建了证书, 然后我把证书放在windows证书管理后,用chrome打开发现提示这个: 虽然可以点击下面的强制 访问 但是chrome ...
- WindowsApps目录占用大量空间
WindowsApps目录占用大量空间今天遇到一个客户端的问题.Windows 10的电脑100G的C盘空间几乎耗尽.但是选取所有文件后总大小只有不到40G.按常规,肯定是有一些没有权限的文件夹的体积 ...
- 大根堆的pop&remove&initialize
1. 定义 [max(min) tree] 一棵树, 其中每个节点的值都大于 (小于) 或等于其 children (如果有) 的值. [max(min) heap] max(min) tree + ...
- 100个Linux Shell脚本经典案例(附PDF)
转载自:https://mp.weixin.qq.com/s/tCKAM67_7K7q2vJthaIsDQ 原文链接:https://wenku.baidu.com/view/4f089430a116 ...