• 驱动通信基本框架的实例


    1.Ring0驱动层代码的编写:

    //codemsg.h 通信控制码的定义
    #ifndef _DEFINE_H_
    #define _DEFINE_H_
    
    // _number:    0 -> 2047 : reserved for Microsoft 微软保留
    //             2047 -> 4095 : reserved for OEMs 用户自定义     
    #define CODEMSG(_number) CTL_CODE(FILE_DEVICE_UNKNOWN, _number , METHOD_BUFFERED,
                                    FILE_READ_DATA | FILE_WRITE_DATA)       
    //定义控制码
    #define INIT_FILE_NAME 2047
    
    #endif
    
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    //Ring0.h
    #ifndef _KERNEL_MODULE_H_
    #define _KERNEL_MODULE_H_
    
    #include <ntifs.h>
    #include "codemsg.h"
    
    //设备对象的名称
    #define DEVICE L"\Device\www.AntiGameProtect.com"
    
    //链接符号的名称
    #define DOSDEVICE L"\DosDevices\www.AntiGameProtect.com"
    
    PDEVICE_OBJECT DriverDeviceObject;
    
    #endif
    
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    //Ring0.c
    #include "Ring0.h"
    
    //驱动卸载例程函数,在这里释放一些资源。
    VOID DriverUnload(PDRIVER_OBJECT DriverObject)
    {
    	UNICODE_STRING DeviceName;
    	UNICODE_STRING DosDeviceName; 
    
    	//删除符号链接
    	RtlInitUnicodeString(&DosDeviceName, DOSDEVICE);
    	IoDeleteSymbolicLink(&DosDeviceName );
    
    	//删除驱动对象
    	if(DriverDeviceObject != NULL)
    		IoDeleteDevice(DriverDeviceObject);
    
    	DbgPrint("驱动卸载成功!
    ");
    }
    
    //默认的例程处理函数
    NTSTATUS IODispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    {
    	Irp->IoStatus.Status = STATUS_SUCCESS;
    	IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    	return STATUS_SUCCESS;
    }
    
    //验证一个WCHAR内容的指针是否可以访问
    BOOLEAN ValidateWCHARString(WCHAR *pwzStr, ULONG_PTR Length)
    {
    	ULONG i;
    
    	__try
    	{
    		//第一步判断指针和大小是否为NULL,是的话就没必要验证了
    		if (*pwzStr == NULL || Length == 0)
    		{
    			return FALSE;
    		}
    
    		//以length长度循环检查指针pwzStr里面的值
    		for (i = 0; i < Length; i++)
    		{
    			//检查内存是否可以访问。
    			if (!MmIsAddressValid((PUCHAR)pwzStr + i))
    			{
    				//只要有一个字节是不可读取,就失败
    				return FALSE;
    			}
    		}
    
    	}__except(EXCEPTION_EXECUTE_HANDLER)
    	{
    		//触发了异常
    		return FALSE;
    	}
    
    	return TRUE;
    }
    
    //IRP通信例程处理函数
    NTSTATUS IOManager(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    {   
    	//获取当前IrpStack,通过读取其结构成员,取出我们需要的控制码IRPcode
    	PIO_STACK_LOCATION StackLocation = IoGetCurrentIrpStackLocation(Irp);
    
    	//获取控制码IRPcode
    	ULONG IRPcode = StackLocation->Parameters.DeviceIoControl.IoControlCode;  
    
    	WCHAR *buf;
    	SIZE_T size;
    	WCHAR *pwzCopyBuf = NULL;
    
    	//获取应用层传进来的内存缓冲区
    	buf = (WCHAR*)Irp->AssociatedIrp.SystemBuffer;
    
    	//内存缓冲区的字节长度
    	size  = (SIZE_T)Irp->Size;
    
    	//设置Irp的状态
    	Irp->IoStatus.Status = STATUS_SUCCESS; 
    
    	switch(IRPcode)
    	{
    		case CODEMSG(INIT_FILE_NAME):
    
    		//从应用层传进来的buf,你无法预知这个值是否可以访问,所以,这里要验证我们传递进来的buf的有效性,才可以在驱动层访问buf
    		//所以,这里我写了一个ValidateWCHARString来验证这个变量
    		__try{
    			//判断Buffer的有效性
    			if (ValidateWCHARString(buf, size))
    			{
    
    				//提示-应用层数据传到了驱动层
    				DbgPrint("Buf ==> %ws:%d
    ", buf, size);
    
    				//申请内存,类似应用层的new,并给于标识'fp'
    				pwzCopyBuf = (WCHAR*)ExAllocatePoolWithTag(NonPagedPool, size, 'fp');
    
    				//如果申请内存成功
    				if (pwzCopyBuf)
    				{
    					//内存初始化
    					memset(pwzCopyBuf, 0, size);
    
    					//copy到我们新申请的内存
    					memcpy(pwzCopyBuf,buf,size);
    
    					//显示从应用层获取到的字符串信息
    					DbgPrint("CopyBuf ==> %ws
    ", pwzCopyBuf);
    
    					//在驱动下面用C语言来写,就要遵守windows的规则。申请的内存,必须要释放。
    					//要记得释放内存
    					ExFreePool(pwzCopyBuf);
    				}
    			}
    		}
    		__except (EXCEPTION_EXECUTE_HANDLER)
    		{
    			//获取异常的状态码
    			Irp->IoStatus.Status = GetExceptionCode();
    		}
    		break;
    
    	default:
    		Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;                       
    
    	}
    
    	//设置Irp的返回码
    	IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    	return Irp->IoStatus.Status;
    }
    
    
    //驱动的入口函数DriverEntry
    NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING theRegistryPath)
    {
    	UNICODE_STRING DeviceName;
    	UNICODE_STRING DosDeviceName; 
    	NTSTATUS status;
    
    	//初始化驱动符号名
    	//UNICODE_STRING是一个结构体,类似win32里面的结构体,比如SYSTEMTIME
    	//在Win系统下,内核的结构体都是可以通过windbg或者wrk获得
    	/*
    	lkd> dt_unicode_string
    	nt!_UNICODE_STRING
    	+0x000 Length           : Uint2B //文本长度
    	+0x002 MaximumLength    : Uint2B //最大长度
    	+0x004 Buffer           : Ptr32 Uint2B //文本内容,是unicode类型,即WCHAR
    	*/
    
    	//设备名称字符串
    	RtlInitUnicodeString(&DeviceName, DEVICE);
    	//符号链接字符串
    	RtlInitUnicodeString(&DosDeviceName, DOSDEVICE);
    
    	//创建设备对象
    	status = IoCreateDevice(
    		DriverObject,        // ptr to caller object
    		0,                   // extension device allocated byte number
    		&DeviceName,         // device name 
    		FILE_DEVICE_UNKNOWN, 
    		0,                   // no special caracteristics
    		FALSE,               // we can open many handles in same time
    		&DeviceObject);		 // [OUT] ptr to the created object
    
    	if (!NT_SUCCESS(status))
    	{
    		return STATUS_NO_SUCH_DEVICE;
    	}
    	//同样也需要一个符号链接,不然会影响到驱动和应用层的通信
    	status = IoCreateSymbolicLink(&DosDeviceName,&DeviceName);
    	if(!NT_SUCCESS(status))
    	{
    		IoDeleteDevice(DriverDeviceObject);
    		return STATUS_NO_SUCH_DEVICE;
    	}   
    
    	//设置驱动卸载例程函数
    	DriverObject->DriverUnload = DriverUnload;
    
    	//IRP_MJ_CREATE,响应的是应用层函数CreateFile,应用层调用这个函数就会进入这个例程
    	DriverObject->MajorFunction[IRP_MJ_CREATE] = IODispatch;
    
    	//下面的分别对应应用层CloseHandle、ReadFile、WriteFile函数
    	DriverObject->MajorFunction[IRP_MJ_CLOSE]  = IODispatch;
    	DriverObject->MajorFunction[IRP_MJ_READ]   = IODispatch;
    	DriverObject->MajorFunction[IRP_MJ_WRITE]  = IODispatch;
    
    	//一般我们跟应用层通信,都是通过IRP_MJ_DEVICE_CONTROL例程,这个例程对应的是应用层下的DeviceIoControl
    	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IOManager; //DeviceIoControl()
    
    	//设置通信方式--直接方式I/O  
    	DeviceObject->Flags |= DO_BUFFERED_IO;
    	//设置文件字对齐  
    	DeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
    
    	//设备初始化完毕可以工作了  
    	DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    
    	//提示驱动加载成功
    	DbgPrint("Hello Driver !
    ");
    
    	return STATUS_SUCCESS;
    }
    
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    


    2.Ring3应用层代码的编写:


    //Ring3.cpp
    #include <windows.h>
    #include <stdio.h>
    
    //包含控制码的头文件
    #include "..Ring0codemsg.h"
    
    //向驱动发送请求
    BOOL CallDriver(char *ID,char *lpBuffer)
    {
    	HANDLE service = 0;
    	HANDLE device = 0;
    	char ret[1024];
    	WCHAR ToSend[512];
    	DWORD code = -1;
    	DWORD bytes;
    
    	//通过符号连接打开设备对象
    	device = CreateFile("\\.\www.AntiGameProtect.com", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    	if( !device || device==INVALID_HANDLE_VALUE )
    	{   
    		printf("打开驱动失败,驱动加载不成功. %d
    ",GetLastError());
    		return FALSE; 
    	}
    
    	//判断输入的命令是否是"-file"
    	if(!strcmp(ID,"-file"))
    	{
    		//对应我们驱动下面的控制码。
    		code = INIT_FILE_NAME; 
    	}
    
    	//判断驱动的控制码是否有效
    	if (code == -1)
    	{
    		printf("无效的ID
    ");
    		return FALSE; 
    	}
    
    	//将ascii码lpBuffer字符串转unicode码字符串ToSend
    	MultiByteToWideChar(CP_ACP, 0, lpBuffer, -1, ToSend, sizeof(ToSend));
    
    	DeviceIoControl(device,
    		CODEMSG(code),         //驱动的控制码
    		ToSend,                //输入缓冲区
    		(wcslen(ToSend)+1)*2,  //输入缓冲区的大小
    		&ret,                  //输出缓冲区
    		sizeof(ret),           //输出缓冲区的大小
    		&bytes,                //返回的字节数
    		NULL);
    
    	//关闭驱动文件
    	CloseHandle(device);
    
    	printf("完成!
    ");
    
    	return TRUE;
    }
    
    //main函数
    void main(int argc,char *argv[])
    {
    	//判断用户输入的合法性
    	if (argc != 3)
    	{
    		printf("Example:%s ID CommandLine
    ",argv[0]);
    		return;
    	}
    
    	//调用驱动的代码
    	CallDriver(argv[1], argv[2]);
    
    	return;
    }
    



  • 相关阅读:
    springboot搭建环境访问Controller层返回404
    SpringMVC使用注解@RequestMapping映射请求
    Redis数据类型
    mysql小结
    将数据四舍五入到十位
    Repeated DNA Sequences
    Reverse Linked List II
    Shortest Palindrome
    Single Number
    Sort Colors
  • 原文地址:https://www.cnblogs.com/csnd/p/11800724.html
Copyright © 2020-2023  润新知