在阅读本节前,建议先阅读《Windows内核安全与驱动开发》第五章内容,并自行了解相关背景知识。
创建项目,打开:项目属性->链接器->输入->附加依赖项(点开后选择【编辑】),添加:
%(AdditionalDependencies)
$(DDK_LIB_PATH)\wdmsec.lib
参考代码:
#include
#include
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
#include
#include
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)。
测试图例:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章