Windows7下驱动开发与调试体系构建——2.R3与R0的通信示例

目录/参考资料: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)。


测试图例:

 

posted @ 2021-02-18 16:40  byxiaobai  阅读(485)  评论(0)    收藏  举报