《天书夜读:从汇编语言到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 ...
随机推荐
- 【微信小程序】调用wx.request接口需要注意的问题
写在前面 之前写了一篇<微信小程序实现各种特效实例>,上次的小程序的项目我负责大部分前端后台接口的对接,然后学长帮我改了一些问题.总的来说,收获了不少吧! 现在项目已经完成,还是要陆陆续续 ...
- wxPython中按钮、文本控件的简单运用
本节学习图形用户界面 ------------------------ 本节介绍如何创建python程序的图形用户界面(GUI),也就是那些带有按钮和文本框的窗口.这里介绍wxPython : 下载地 ...
- iOS代码处理横屏问题
借助通知来控制界面的横竖屏切换.还是整个App中大部分界面都是竖屏,某个界面可以横竖屏切换的情况. 首先,在[General]-->[Device Orientation]设置仅支持竖屏,lik ...
- JAVA提高五:注解Annotation
今天我们学习JDK5.0中一个非常重要的特性,叫做注解.是现在非常流行的一种方式,可以说因为配置XML 比较麻烦或者比容易查找出错误,现在越来越多的框架开始支持注解方式,比如注明的Spring 框架, ...
- chromium源码阅读--进程的Message Loop
上一篇总结了chromium进程的启动,接下来就看线程的消息处理,这里的线程包含进程的主进程. 消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的. 主要如下几点 ...
- win10 UWP 申请微软开发者
申请微软开发者可以到https://dev.windows.com/zh-cn/programs/join 如果是学生,先去http://www.dreamspark.com/ 如果是英文,点stud ...
- win10 uwp DataContext
本文告诉大家DataContext的多种绑法. 适合于WPF的绑定和UWP的绑定. 我告诉大家很多个方法,所有的方法都有自己的优点和缺点,可以依靠自己喜欢的用法使用.当然,可以在新手面前秀下,一个页面 ...
- 长话短说 之 js的原型和闭包
原型链:undefined, number, string, boolean 属于简单的值类型,函数.数组.对象.null.new obj()都是引用类型.检测值类型用typeof x 即可,检测引用 ...
- Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解
这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结 ...
- yii2之依赖注入与依赖注入容器
一.为什么需要依赖注入 首先我们先不管什么是依赖注入,先来分析一下没有使用依赖注入会有什么样的结果.假设我们有一个gmail邮件服务类GMail,然后有另一个类User,User类需要使用发邮件的功能 ...