《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求
1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置。
2)DRIVER_OBJECT下的派遣函数(分发函数)指针的个数为:IRP_MJ_MAXINUM_FUNCTION,保存在一个数组中,编写驱动程序,实质上就是自己编写处理I/O请求IRP包的派遣函数,可以使用for循环将所有的派遣函数注册到同一个派遣函数。分发函数类似MFC消息机制中的回调函数,它的名字也是程序员自定义的,但是参数确定。卸载函数通过地址在驱动对象的DriverUnload域,在驱动卸载中会自动调用该函数。由于DriverUnload函数没有返回值,所以不能决定是否真正被卸载,只能做一些善后处理。
3)驱动程序和系统其它组件之间的交互是通过给设备对象(DEVICE_OBJECT)发送或者接受发送给设备对象的请求(IRP包)来交互的(设备对象通过符号链接使其在运用层可见,如果不创建符号链接,则只在内核可见),一个驱动程序不创建设备,就不会受到任何IRP,分发函数也就失去了意义。但在一些只要HOOK等不需要交互的驱动,可以不创建设备。运用层使用符号链接可通过CreateFile像打开文件一样打开设备,只不过路径格式为“\\\\.\\设备符号链接”。
4)IoCreateDevice生成的设备有权限限制,使用IoCreateDeviceSecure(在wdmsec.lib中)可以对所有用户开放权限,函数原型为:
NTSTATUS IoCreateDeviceSecure( IN PDRIVER_OBJECT DriverObject, IN ULONG DeviceExtensionSize, IN PUNICODE_STRING DeviceName OPTIONAL, IN DEVICE_TYPE DeviceType, IN ULONG DeviceCharacteristics, IN BOOLEAN Exclusive, IN PCUNICODE_STRING DefaultSDDLString, IN LPCGUID DeviceClassGuid, OUT PDEVICE_OBJECT *DeviceObject );
这个函数增加了两个内容(注:亲测不通过,不知道还需要导入什么lib文件而作者没提到):
a)DefaultSDDLString:描述权限的字符串,使用“D:P(A;;GA;;;WD)”即可对所有用户开放权限,如:UNICODE_STRING sddl = RTL_CONSTANT_STRING(L” D:P(A;;GA;;;WD)”)
b)DeviceClassGuid:随便填写GUID,不与其他设备冲突即可。如:const GUID DECLSPEC_SELECTANY MYGUID_CLASS_MYCDO = {0x26e0d1e0L,0x8189,0x12e0,{0x99,0x14,0x88,0x00,0x22,0x30,0x19,0x13}}
5)在\DosDevices\(注:由《驱动开发技术详解》可知,WDM驱动为\DosDevice\,而ddk驱动为\??\,我测试使用的是ddk,作者并未说明,而如下代码暂时未测试)下的简单符号链接并不是对所有用户可见,但是在DriverEnter中生成符号链接则对所有用户可见,因为入口函数总是在进程“System”中,但是注销后可能消失。解决办法:生成全局符号链接“\DosDevice\Global\符号链接”即可,在不支持符号链接用户相关性的系统上,使用如下代码:
UNICODE_STRING device_name; UNICODE_STRING symbl_name; , 0x10) ) { //如果支持符号链接用户相关,用全局符号链接 RtlInitUnicodeString(&symbl_name,L” \\DosDevice\\Global\\符号链接”); } else { //如果不支持,则不用全局的 RtlInitUnicodeString(&symbl_name,L” \\DosDevice\\符号链接”); } IoCreateSymbolicLink(&symbl_name,&device_name);
6)IRP类型取决于主功能号,也就是DRIVER_OBJECT中分发函数指针数组中的索引(如IRP_MJ_CREATE等)。IRP主功能号在IRP的当前栈空间。打开设备IRP可简单返回成功,这需要3个步骤(返回失败也一样):
a)设置pIrp->IoStatus.Information=0
b)设置pIrp->IoStatus.Status=STATUS_SUCCESS(如果要让处理失败,则返回错误代码)
c)调用IoCompleteRequest(pIrp,IO_NO_INCREMENT)这个函数指示完成IRP处理
完成以上3步以后可直接返回pIrp->IoStatus.Status。
7)运用层消息传入:可使用WriteFile,也可以使用DeviceIoControl(设备控制接口),后者是双向的。DeviceIoControl的IRP携带了控制码(必须预先宏定义)、输入缓冲区位置和长度,以及输出缓冲区和位置长度,使用示例(注:DDk驱动代码,实现的功能是在R3层MFC运用程序点击一个按钮,将发送一个字符串到R0内核程序,内核程序接收字符串并打印它):
//最后修改13-10-15 #include "xde.h" #include <ntddk.h> #define INITCODE code_seg("INIT") /*指的代码运行后就从内存释放掉*/ #define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/ #define DEVNAME L"\\Device\\testDDk_Device" #define SYMNAME L"\\??\\TestLinkName" //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序写,内核读数据时的控制码宏定义 #define MY_DVC_IN_CODE (ULONG)CTL_CODE(\ FILE_DEVICE_UNKNOWN,/*设备类型*/\ 0xa01,/*用户自定义功能号*/\ METHOD_BUFFERED,/*传输方式:带缓冲区*/\ FILE_WRITE_DATA/*调用者需求权限:文件写*/) typedef unsigned long ULONG; //函数声明 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject);//创建设备对象与符号链接 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数 DRIVER_INITIALIZE DriverEntry;//入口,添加设备,启动HOOk DRIVER_UNLOAD DriverUnload;//卸载,删除设备,停止HOOk ///////////////////////////////////////////////////////////////// #pragma PAGECODE //DriverEntry,入口函数。相当于main NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING sRegPath) { //创建设备对象与符号链接 CreateMyDevice (pDriverObject); //派遣函数 pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数 pDriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } /////////////////////////////////////////////////////////////////// #pragma PAGECODE //提供一个Unload函数值是为了让这个程序能动态卸载,方便调试 VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT pDev;//用来取得要删除设备对象 UNICODE_STRING symLinkName; // //打印一句 KdPrint(("Our driver is unloading...\n")); pDev=pDriverObject->DeviceObject;//从驱动对象取得设备对象,所有设备对象连成一条链,这里假定只有一个设备 IoDeleteDevice(pDev); //删除设备 RtlInitUnicodeString(&symLinkName,SYMNAME); //删除符号链接 IoDeleteSymbolicLink(&symLinkName); KdPrint(("驱动成功被卸载... ")); //sprintf,printf //取得要删除设备对象 //删掉所有设备 } //读取IRP_MJ_DEVICE_CONTROL类型IRP包中的数据信息:驱动读 NTSTATUS MyDeviceIoCtrlIn(PIRP pIrp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); //得到输入缓存长度 ULONG in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; //输入输出缓冲是公用内存的 PVOID buffer = pIrp->AssociatedIrp.SystemBuffer; //输出缓冲区信息 KdPrint(("The Messege from R3 is : %s \n",buffer)); pIrp->IoStatus.Information = in_len;//设置IRP读取的字节数 pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理 return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致 } //分类处理 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp) { //IoGetCurrentIrpStackLocation得到调用者堆栈的指针 PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); //判断IRP请求包的类型 switch(irpsp->MajorFunction) { case IRP_MJ_CREATE://处理打开请求(CreateFile) KdPrint(("IRP_MJ_CREATE\n")); break; case IRP_MJ_CLOSE://处理关闭请求(CloseHandle) KdPrint(("IRP_MJ_CLOSE\n")); break; case IRP_MJ_READ://处理读请求(ReadFile) KdPrint(("IRP_MJ_READ\n")); break; case IRP_MJ_WRITE://处理写请求(WriteFile) KdPrint(("IRP_MJ_WRITE\n")); break; case IRP_MJ_DEVICE_CONTROL://处理设备控制信息(DeviceIoControl) { //首先得到功能号 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; KdPrint(("IRP_MJ_DEVICE_CONTROL\n")); if ( code == MY_DVC_IN_CODE )//内核读取数据 { KdPrint(("MY_DVC_IN_CODE\n")); return MyDeviceIoCtrlIn(pIrp); } break; } default: KdPrint(("其它处理\n")); } pIrp->IoStatus.Information=;//设置IRP操作的字节数为,这里无实际意义 pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理 return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致 } #pragma INITCODE NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj;/*用来返回创建设备结构体的指针*/ //设备对象(DEVICE_OBJECT)由驱动创建。一个驱动可以创建多个设备对象。 //通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象。 //一个驱动创建的所有设备对象链成一条链。该驱动的驱动对象可以找到这个链, //一个设备对象也可以找到创建它的驱动的驱动对象。DEVICE_OBJECT是设备对象存在的形式 //创建设备名称 UNICODE_STRING devName; UNICODE_STRING symLinkName; // 结构体,包含了宽字节字符缓冲区与其长度 RtlInitUnicodeString(&devName,DEVNAME);//对devName初始化字串为"\\Device\\testDDK_Device" //这个宽字节的路径“\\Device\\ ”部分不能改变,后面是设备名称 //创建设备对象 status = IoCreateDevice ( pDriverObject, //驱动程序对象指针。在入库函数DriverEntry过程里接收 ,//指定驱动程序为设备扩展对象定义的结构体大小 &devName,//设备名称,必须是完整的设备路径名,设置为NULL则是无名设备 FILE_DEVICE_UNKNOWN,//设备类型 , TRUE,//驱动程序的其它信息以及指定设备是否是独占的,TRUE则是 &pDevObj);//输出,用来保存PDEVICE_OBJECT结构体指针,这个指针指向设备对象自身 if (!NT_SUCCESS(status)) { if (status==STATUS_INSUFFICIENT_RESOURCES) { KdPrint(("资源不足STATUS_INSUFFICIENT_RESOURCES")); } if (status==STATUS_OBJECT_NAME_EXISTS ) { KdPrint(("指定对象名存在")); } if (status==STATUS_OBJECT_NAME_COLLISION) { KdPrint(("//对象名有冲突")); } KdPrint(("设备创建失败...++++++++")); return status; } KdPrint(("设备创建成功...++++++++")); // IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中 pDevObj->Flags |= DO_BUFFERED_IO; //创建符号链接 RtlInitUnicodeString(&symLinkName,SYMNAME); status = IoCreateSymbolicLink( &symLinkName,// Unicode字符串指针,是一个用户态可见的名称 &devName );// Unicode字符串指针,是驱动程序创建的设备对象名称。 if (!NT_SUCCESS(status)) /*status等于*/ { IoDeleteDevice( pDevObj );//删除驱动设备 return status; } return STATUS_SUCCESS; }
在R3运用程序上,进行DeviceIoControl代码(写MFC程序,添加按钮点击事件):
//在头文件添加 #include <winioctl.h> #define SYMNAME "\\\\.\\TestLinkName" //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序写,内核读数据时的控制码宏定义 #define MY_DVC_IN_CODE (ULONG)CTL_CODE(\ FILE_DEVICE_UNKNOWN,/*设备类型*/\ 0xa01,/*用户自定义功能号*/\ METHOD_BUFFERED,/*传输方式:带缓冲区*/\ FILE_WRITE_DATA/*调用者需求权限:文件写*/) //添加按钮点击事件 // TODO: 在此添加控件通知处理程序代码 DWORD length = ;//返回的长度 HANDLE device = CreateFile(SYMNAME, GENERIC_READ|GENERIC_WRITE,,, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM,); if ( device == INVALID_HANDLE_VALUE ) { //打开失败 AfxMessageBox(_T("打开符号链接失败")); return; } CString message("Hello R0"); LPVOID in_buffer = message.GetBuffer(); int in_buffer_len = message.GetLength(); BOOL ret = DeviceIoControl(device, MY_DVC_IN_CODE, //功能号 in_buffer, //输入缓冲,要传递的信息,预先填好 in_buffer_len, //输入缓冲长度 NULL, //没有输出缓冲 , //输出缓冲为长度 &length, //返回的长度 NULL); if (!ret) { // DeviceIoControl失败 AfxMessageBox(_T("DeviceIoControl失败")); } if ( length != in_buffer_len) { AfxMessageBox(_T("读取字节数不一致")); }
输出效果:
8)驱动层消息传出:运用程序需要开启一个线程调用DeviceIoControl(或者ReadFile)读取数据,而驱动在没有消息的时候阻塞这个IRP,等待有消息时返回(使用事件也行,但还要传递事件句柄,较繁琐)。驱动内部制作一个链表,当有消息的要通知运用程序时,把消息放入链表,并设置同步事件,在DeviceIoControl的处理中等待事件。这里测试一下,避免麻烦,传送一个ULONG类型,传输10次,R3运用程序开线程接收,不停的弹窗,这里不考虑数据覆盖问题。
//内核程序 #include "xde.h" #include <ntddk.h> #define INITCODE code_seg("INIT") /*指的代码运行后就从内存释放掉*/ #define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/ #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DEVNAME L"\\Device\\testDDk_Device" #define SYMNAME L"\\??\\TestLinkName" //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序读,内核写数据时的控制码宏定义 #define MY_DVC_OUT_CODE (ULONG)CTL_CODE(\ FILE_DEVICE_UNKNOWN,/*设备类型*/\ 0xa02,/*用户自定义功能号*/\ METHOD_BUFFERED,/*传输方式:带缓冲区*/\ FILE_READ_DATA/*调用者需求权限:文件读*/) typedef unsigned long ULONG; typedef ULONG* PULONG; //要发送的数据 tatic ULONG data; //全局事件变量 static KEVENT g_my_notify_event; //函数声明 NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject);//创建设备对象与符号链接 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数 void MySleep(LONG msec);//延时函数 void MyThreadProc(PVOID context);//测试线程 DRIVER_INITIALIZE DriverEntry;//入口,添加设备,启动HOOk DRIVER_UNLOAD DriverUnload;//卸载,删除设备,停止HOOk ///////////////////////////////////////////////////////////////// #pragma PAGECODE //DriverEntry,入口函数。相当于main NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING sRegPath) { HANDLE hThread = NULL; NTSTATUS status; //对ZwCreateSection进行HOOK RtlInitUnicodeString(&apiFuncName,L"ZwCreateSection"); if (InlineHookIofCallDriverXp(&apiFuncName,(ULONG)MyIofCallDriver,TRUE) == FALSE) { KdPrint(("Inline Hook failed!\n")); } //创建设备对象与符号链接 CreateMyDevice (pDriverObject); //派遣函数 pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CONTROL;//IRP_MJ_CREATE相关IRP处理函数 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数 //创建线程: status = PsCreateSystemThread( &hThread,//新线程句柄 0L,NULL,NULL,NULL, MyThreadProc,//函数地址 NULL//传递参数 ); if ( !NT_SUCCESS(status) ) { KdPrint(("CreateSystemThread failed!")); } ZwClose(hThread); pDriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } void MyThreadProc(PVOID context) { //测试 KeInitializeEvent(&g_my_notify_event, SynchronizationEvent,//自动复位 FALSE//初始为无信号状态 ); ; data <= + ; data ++) { //Sleep(3000) MySleep(); //设置有信号 KeSetEvent(&g_my_notify_event, IO_NO_INCREMENT,//预备给被唤醒线程临时提升优先级的增量 FALSE//之后是否跟KeWaitForXXX等待(其它或者自身)事件 ); KdPrint(("The data is : %u \n",data)); } } void MySleep(LONG msec) { LARGE_INTEGER my_interval; my_interval.QuadPart = DELAY_ONE_MILLISECOND; my_interval.QuadPart *= msec; KeDelayExecutionThread(KernelMode,,&my_interval); } //提供一个Unload函数值是为了让这个程序能动态卸载,方便调试 VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT pDev;//用来取得要删除设备对象 UNICODE_STRING symLinkName; // //打印一句 KdPrint(("Our driver is unloading...\n")); //停止HOOk if(InlineHookIofCallDriverXp(&apiFuncName,(ULONG)MyIofCallDriver,FALSE) == FALSE) { KdPrint(("UnHook failed!\n")); } KdPrint(("UnHook succeed!\n")); pDev=pDriverObject->DeviceObject;//从驱动对象取得设备对象,所有设备对象连成一条链,这里假定只有一个设备 IoDeleteDevice(pDev); //删除设备 RtlInitUnicodeString(&symLinkName,SYMNAME); //删除符号链接 IoDeleteSymbolicLink(&symLinkName); KdPrint(("驱动成功被卸载... ")); //sprintf,printf //取得要删除设备对象 //删掉所有设备 } //读取IRP_MJ_DEVICE_CONTROL类型IRP包中的数据信息:驱动写 NTSTATUS MyDeviceIoCtrlOut(PIRP pIrp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); //获得输出缓冲区 PVOID buffer = pIrp->AssociatedIrp.SystemBuffer; //获取输出缓冲区长度 ULONG out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; //等待有事件信号 KeWaitForSingleObject(&g_my_notify_event, Executive,//等待原因,驱动程序设置为Executive KernelMode,//内核模式 FALSE,//不允许警告 NULL//等待超时时间:无限等待 ); //有请求了 if (out_len < sizeof(ULONG) )//缓冲长度不够 { pIrp->IoStatus.Information = sizeof(ULONG);//写需要的长度 pIrp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return pIrp->IoStatus.Status; } //长度足够,填写输出缓冲区 *((PULONG)buffer) = data; //返回成功 pIrp->IoStatus.Information = sizeof(ULONG);//写入的长度 pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理 return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致 } //分类处理 NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp) { //IoGetCurrentIrpStackLocation得到调用者堆栈的指针 PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); //判断IRP请求包的类型 switch(irpsp->MajorFunction) { case IRP_MJ_CREATE://处理打开请求(CreateFile) KdPrint(("IRP_MJ_CREATE\n")); break; case IRP_MJ_CLOSE://处理关闭请求(CloseHandle) KdPrint(("IRP_MJ_CLOSE\n")); break; case IRP_MJ_READ://处理读请求(ReadFile) KdPrint(("IRP_MJ_READ\n")); break; case IRP_MJ_WRITE://处理写请求(WriteFile) KdPrint(("IRP_MJ_WRITE\n")); break; case IRP_MJ_DEVICE_CONTROL://处理设备控制信息(DeviceIoControl) { //首先得到功能号 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode; KdPrint(("IRP_MJ_DEVICE_CONTROL\n")); if ( code == MY_DVC_OUT_CODE)//内核传出数据 { KdPrint(("MY_DVC_OUT_CODE\n")); return MyDeviceIoCtrlOut(pIrp); } break; } default: KdPrint(("其它处理\n")); } //判断是输入还是输出操作 pIrp->IoStatus.Information=;//设置IRP操作的字节数为,这里无实际意义 pIrp->IoStatus.Status=STATUS_SUCCESS;//设置IRP处理状态 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP的处理 return STATUS_SUCCESS; //返回成功,这样,发起I/O操作的Win32API将会返回TRUE,使用GetLastError和设置的IPR处理状态一致 } ///////////////////////////////////////////////////////////////// #pragma INITCODE NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj; //创建设备名称 UNICODE_STRING devName; UNICODE_STRING symLinkName; RtlInitUnicodeString(&devName,DEVNAME); //创建设备对象 status = IoCreateDevice/*Secure*/( pDriverObject, //驱动程序对象指针。在入库函数DriverEntry过程里接收 ,//指定驱动程序为设备扩展对象定义的结构体大小 &devName,//设备名称,必须是完整的设备路径名,设置为NULL则是无名设备 FILE_DEVICE_UNKNOWN,//设备类型 , TRUE,//驱动程序的其它信息以及指定设备是否是独占的,TRUE则是 //&sddl,(LPCGUID)&MYGUID_CLASS_MYCDO,//sddl,guid &pDevObj);//输出,用来保存PDEVICE_OBJECT结构体指针,这个指针指向设备对象自身 if (!NT_SUCCESS(status)) { if (status==STATUS_INSUFFICIENT_RESOURCES) { KdPrint(("资源不足STATUS_INSUFFICIENT_RESOURCES")); } if (status==STATUS_OBJECT_NAME_EXISTS ) { KdPrint(("指定对象名存在")); } if (status==STATUS_OBJECT_NAME_COLLISION) { KdPrint(("//对象名有冲突")); } KdPrint(("设备创建失败...++++++++")); return status; } KdPrint(("设备创建成功...++++++++")); // IoCreateDevice 会把新创建的这个设备对象,链入驱动的设备链中 pDevObj->Flags |= DO_BUFFERED_IO; //创建符号链接 RtlInitUnicodeString(&symLinkName,SYMNAME); status = IoCreateSymbolicLink( &symLinkName,// Unicode字符串指针,是一个用户态可见的名称 &devName );// Unicode字符串指针,是驱动程序创建的设备对象名称。 if (!NT_SUCCESS(status)) /*status等于*/ { IoDeleteDevice( pDevObj );//删除驱动设备 return status; } return STATUS_SUCCESS; }
R3接收程序:
#include <winioctl.h> #define SYMNAME "\\\\.\\TestLinkName" //IRP_MJ_DEVICE_CONTROL类型IRP用于R3运用程序读,内核写数据时的控制码宏定义 #define MY_DVC_OUT_CODE (ULONG)CTL_CODE(\ FILE_DEVICE_UNKNOWN,/*设备类型*/\ 0xa02,/*用户自定义功能号*/\ METHOD_BUFFERED,/*传输方式:带缓冲区*/\ FILE_READ_DATA/*调用者需求权限:文件读*/) DWORD MyThreadProc(void); //按钮事件 void CMFCTestDlg::OnBnClickedButton1() { CreateThread(NULL,,(LPTHREAD_START_ROUTINE)MyThreadProc,NULL,NULL,NULL); } DWORD MyThreadProc(void) { // TODO: 在此添加控件通知处理程序代码 ULONG data = ;//返回的数据 DWORD length = ;//返回的长度 HANDLE device = CreateFile(SYMNAME, GENERIC_READ|GENERIC_WRITE,,, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM,); if ( device == INVALID_HANDLE_VALUE ) { //打开失败 AfxMessageBox(_T("打开符号链接失败")); return FALSE; } ; ) { BOOL ret = DeviceIoControl(device, MY_DVC_OUT_CODE, //功能号 NULL, //没有输入缓冲,要传递的信息,预先填好 NULL, //输入缓冲长度为 &data, //输出缓冲 sizeof(ULONG), //输出缓冲长度 &length, //返回的长度 NULL); if (!ret) { // DeviceIoControl失败 AfxMessageBox(_T("DeviceIoControl失败")); } CString a; a.Format("%u",data); AfxMessageBox(a); } CloseHandle(device); return TRUE; }
效果图:
《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求的更多相关文章
- 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建
(原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...
- 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作
1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...
- 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序
---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...
- 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建
1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...
- 《天书夜读:从汇编语言到windows内核编程》十 线程与事件
1)驱动中使用到的线程是系统线程,在system进程中.创建线程API函数:PsCreateSystemThread:结束线程(线程内自行调用)API函数:PsTerminateSystemThrea ...
- 《天书夜读:从汇编语言到windows内核编程》九 时间与定时器
1)使用如下自定义函数获取自系统启动后经历的毫秒数:KeQueryTimeIncrement.KeQueryTickCount void MyGetTickCount(PULONG msec) { L ...
- 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存
1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...
- 《天书夜读:从汇编语言到windows内核编程》三 练习反汇编C语言程序
1) Debug版本算法反汇编,现有如下3×3矩阵相乘的程序: #define SIZE 3 int MyFunction(int a[SIZE][SIZE],int b[SIZE][SIZE],in ...
- 《天书夜读:从汇编语言到windows内核编程》二 C语言的流程与处理
1) Debug与Release的区别:前者称调试版,后者称发行版.调试版基本不优化,而发行版会经过编译器的极致优化,往往与优化前的高级语言执行流程会大相径庭,但是实现的功能是等价的. 2) 如下fo ...
随机推荐
- 使用svn控制系统的优缺点和注意事项
1.当无法连接到中央版本库的环境下,你无法提交代码,将代码加入版本控制.公司一般是局域网,所以使用环境问题不大. 2.svn的备份要备份所有代码数据以及所有更改的版本记录. 3.svn服务端运行方式: ...
- 机器学习理论提升方法AdaBoost算法第一卷
AdaBoost算法内容来自<统计学习与方法>李航,<机器学习>周志华,以及<机器学习实战>Peter HarringTon,相互学习,不足之处请大家多多指教! 提 ...
- FPGA IN 消费电子
消费电子: 消费电子(Consumer electronics),指供日常消费者生活使用的电子产品.消费类电子产品是指用于个人和家庭与广播.电视有关的音频和视频产品,主要包括:电视机.影碟机(VCD. ...
- 远程连接MySQL,防火墙阻止访问,解决方法
远程连接MySQL,防火墙阻止访问,解决方法 xp/2003添加防火墙例外端口 打开防火墙,选择例外选项卡,添加端口 名称:mysqlport 端口号:3306 选中TCP win7添加防火墙例外 ...
- SQL 2008 外网访问说明
1. 安装SQL2008 . 安装SQL2008之前,必须预先安装.NET Framework 3.5,和Windows Installer 4.5 Redistributable. 可能产生错误: ...
- linux RPM软件包管理
linux RPM软件包管理 目录 1.软件包的介绍 2.rpm包管理 3.rpm包管理前端工具 1.软件包的介绍 在我们在对软件包管理前,先对软件包有个很好的了解,这样更方便我们来对其进行管理. 软 ...
- Java 对象复制
Java 对象的一共有 3 种复制对象的方式. 1.直接赋值 (引用复制 ),此种复制方式比较常用. 诸如 A a = b ; a 是直接复制了b的引用 ,也就是说它俩指向的是同一个对象. 此时 ...
- stringstream缓存正确清除方法
当需要清空stringsteam缓存的时候,到底是.str("")呢还是.clear(); 实际上,我认为,保守起见,两者都需要 clear() 是清除 state flag st ...
- Kotlin——最详细的数据类型介绍
任意一种开发语言都有其数据类型,并且数据类型对于一门开发语言来说是最基本的构成,同时也是最基础的语法.当然,kotlin也不例外.kotlin的数据类型和Java是大致相同的,但是他们的写法不同,并且 ...
- 【转】ARM vs X86 – Key differences explained!
原文:http://www.androidauthority.com/arm-vs-x86-key-differences-explained-568718/ Android supports 3 d ...