目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html

在阅读本节前,建议先阅读《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的通信示例的更多相关文章

  1. Windows7下驱动开发与调试体系构建——0.概述

    本文集内容为windows7x64下驱动开发与调试体系构建,内容目录如下: 1.驱动开发的环境准备 2.R3与R0的通信示例 3.自建调试体系概述 4.在x64下使用汇编代码 5.实战反调试标记位(N ...

  2. Windows7下驱动开发与调试体系构建——3.调试体系概述

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 调试体系概述 0.什么是自建调试体系? 就是复写windows的调试api,使得调试 ...

  3. Windows7下驱动开发与调试体系构建——1.驱动开发的环境准备

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 系统基础环境 开发环境 win7下开发驱动需要安装vs,这里使用2017. 安装vs ...

  4. Windows7下驱动开发与调试体系构建——5.实战反调试标记位(NtGlobalFlag)

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html <加密与解密>P670中,介绍了检查程序是否被调试的第二种方法:查看进程 ...

  5. Windows7下驱动开发与调试体系构建——4.在x64下使用汇编代码(x86下的_asm)

    目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html asm文件设置 在vs x64中无法使用_asm关键字,需要使用.asm文件. 按第 ...

  6. 2013-6-2 [转载自CSDN]如何入门Windows系统下驱动开发

    [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文为主,这样让很多驱动初学者很头疼.本人从事驱动开发时间不长也不短,大概 ...

  7. 如何正确入门Windows系统下驱动开发领域?

    [作者]猪头三个人网站 :http://www.x86asm.com/ [序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都以英文 ...

  8. 驱动开发 - WDK 调试及 SVN 环境搭建

    由于从公司辞职了,所以以前在公司里搭建的驱动开发环境也就 Game Over 了, 同样由于那环境是很久以前搭建的,自己也有很多记不清楚的地方了, 而且其中还是有很多需要注意的地方的,所以在这里顺便做 ...

  9. Windows7下QT5开发环境搭建 分类: QT开发 2015-03-09 23:44 65人阅读 评论(0) 收藏

    Windows7下QT开法环境常见搭配方法有两种. 第一种是:QT Creator+QT SDK: 第二种是:VS+qt-vs-addin+QT SDK: 以上两种均可,所需文件见QT社区,QT下载地 ...

随机推荐

  1. [开源内卷] .NET 定时任务 -- FreeScheduler 支持 cron、持久化、可变定时设置

    前言 卷了,卷了,卷了,最近太卷...这篇文章写了好几天了,由于同类型文章太多,排期到今天发布.实在不想卷,得罪了!各位定时任务开源大佬们! .NET 定时组件生态实在太强大了,写下此文只希望能供大家 ...

  2. CCF NOI Online 2021 提高组 T2 积木小赛 (子序列自动机+后缀自动机,O(n^2))

    题面 Alice 和 Bob 最近热衷于玩一个游戏--积木小赛. Alice 和 Bob 初始时各有 n 块积木从左至右排成一排,每块积木都被标上了一个英文小写字母. Alice 可以从自己的积木中丢 ...

  3. C与C++有什么区别

    C是一个结构化语言,它的侧重点在于算法和数据结构.对语言本身而言,C是C++的一个子集. C程序的设计首要考虑的是如何通过一个过程,对输入进行运算处理,得到输出. 对于C++,首要考虑的是如何构造一个 ...

  4. 学习ASP.NET Core Blazor编程系列二——第一个Blazor应用程序(中)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 四.创建一个Blazor应用程序 1. 第一种创 ...

  5. 【题解笔记】PTA基础6-10:阶乘计算升级版

    题目地址:https://pintia.cn/problem-sets/14/problems/742 前言 咱目前还只能说是个小白,写题解是为了后面自己能够回顾.如果有哪些写错的/能优化的地方,也请 ...

  6. KingbaseES V8R6集群同步模式synchronous参数配置详解

    如下图所示: 集群数据同步原理说明: synchronous参数配置测试: 集群节点信息: ID | Name | Role | Status | Upstream | repmgrd | PID | ...

  7. [CG] 顶点动画贴图 (Vertex Animation Texture, VAT)

    什么是顶点动画? 简单来说,通过改变网格顶点的位置,使网格变形从而做成的动画.顶点动画的灵活度要远远高于骨骼动画.骨骼动画是靠骨骼(一堆有层级结构的节点,数量应该是远远小于网格顶点的数量的)的变化来驱 ...

  8. 01-MyBatisPlus简介

    一.简介 官网:http://mp.baomidou.com/ 参考教程:https://baomidou.com/pages/24112f/ MyBatis-Plus(简称 MP)是一个 MyBat ...

  9. 《Java基础——线程类》

    Java基础--线程类       一.线程的创建之Thread类: 规则: 通过声明一个新类作为子类继承 Thread 类,并复写 run() 方法,就可以启动新线程并执行自己定义的 run()方法 ...

  10. git pull提示如下信息时候的操作

    执行git pull时提示信息如下: There is no tracking information for the current branch. Please specify which bra ...