目录/参考资料: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. 微服务性能分析|Pyroscope 在 Rainbond 上的实践分享

    随着微服务体系在生产环境落地,也会伴随着一些问题出现,比如流量过大造成某个微服务应用程序的性能瓶颈.CPU利用率高.或内存泄漏等问题.要找到问题的根本原因,我们通常都会通过日志.进程再结合代码去判断根 ...

  2. ES6 Promise详解

    前言 本文主要是对Promise本身的用法做一个全面解析而非它的原理实现,如果你对Promise的用法还不是很熟悉或者想加深你对Promise的理解,我相信这篇文章一定会帮到你. 首先让我们先了解一下 ...

  3. Linux操作系统学习(运维必会)

    Linux一切皆文件,最高权限的账户root. 1.开机登录 开机会启动很多进程,在Windows上叫"服务"(service),在Linux上叫做"守护进程" ...

  4. JavaScript的入门

    书写的三种方式 1. 书写在script标签里面(一般会用到的) 2.书写在js文件里面(推荐)定义一个js文件(xxx.js) 3. 书写对应的事件属性里面(比较少用) 初体验 1. 目前js的代码 ...

  5. PerfView专题 (第十二篇):对 C# 下的 SDK 类库进行监控(大结局)

    一:背景 本篇是我们系列文章的最后一篇,前面的文章中大多是在 CLR Runtime 以及 OS 层面进行监控来发现各种可疑的程序问题,除了这两个层面,其实我们还可以对 SDK 中一些类进行洞察,比如 ...

  6. Sentinel控制台1.8.3修改源码,修改配置后推送到Nacos

    目录 1. 接着上一篇 2. 思路 3. 下载Sentinel源码 4. 看Gateway里面读取的配置信息 5. 修改Sentinel控制台源码 6. 熔断规则测试 7. 限流规则测试 8. 打包使 ...

  7. wsl创建证书让chrome浏览器识别

    我在wsl里面测试http2协议的时候,在wsl用openssl创建了证书, 然后我把证书放在windows证书管理后,用chrome打开发现提示这个: 虽然可以点击下面的强制 访问 但是chrome ...

  8. WindowsApps目录占用大量空间

    WindowsApps目录占用大量空间今天遇到一个客户端的问题.Windows 10的电脑100G的C盘空间几乎耗尽.但是选取所有文件后总大小只有不到40G.按常规,肯定是有一些没有权限的文件夹的体积 ...

  9. 大根堆的pop&remove&initialize

    1. 定义 [max(min) tree] 一棵树, 其中每个节点的值都大于 (小于) 或等于其 children (如果有) 的值. [max(min) heap] max(min) tree + ...

  10. 100个Linux Shell脚本经典案例(附PDF)

    转载自:https://mp.weixin.qq.com/s/tCKAM67_7K7q2vJthaIsDQ 原文链接:https://wenku.baidu.com/view/4f089430a116 ...