《天书夜读:从汇编语言到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 ...
随机推荐
- 学习如何看懂SQL Server执行计划(二)——函数计算篇
二.函数计算部分 --------------------标量聚合--------------------/* 标量聚合-主要在聚合函数操作中产生 计算标量:根据行中的现有值计算出一个新值 流聚合:在 ...
- angualrJs清除定时器
angualrJs清除定时器爬坑之路: 今天发现一个奇怪问题,放在自定义指令里边的定时器竟然在页面跳转之后,在另一个页面这个循环定时器还在执行,这肯定是不行的,会影响系统的性能. 我在angular里 ...
- Linux vi 退出&保存/不保存
无论是否退出 vi,均可保存所做的工作.按 ESC 键,确定 vi 是否处于命令模式. 操作 键入 保存,但不退出vi :w 保存并退出vi ...
- 虚拟机中ubuntu-16.04 Linux系统下配置mysql数据库,并在windows下使用navicat远程连接
Linux系统下mysql数据库安装配置步骤: 1.在服务器上安装mysql:sudo apt-get install mysql-server sudo apt-get install mysql- ...
- asp.net数据四舍五入
#region 数据四舍五入 /// <summary> /// 四舍五入 /// </summary> /// <param name="dblnum&quo ...
- Bootstrap表格样式(附源码文件)--Bootstrap
1.表格默认样式 <h4>表格默认样式</h4><table><!--默认样式--> <tr><th>序号</th> ...
- Delphi实现电脑端微信图片文件解密
电脑端微信收到图片后是存在了“C:\Users\系统用户名\Documents\WeChat Files\微信帐号\Data”目录下的,但文件不能直接使用图片浏览器打开的,因为做了一些加密,之前有个朋 ...
- 在SQL Server中实现关系模型的阶梯到级别3的t -SQL DML
在SQL Server中实现关系模型的阶梯到级别3的t -SQL DML 格雷戈里·拉森(Gregory Larsen),2017/08/02(第一次出版:2011 /11/09) 原文链接:http ...
- Xamarin.Forms 开发IOS、Android、UWP应用
C#语言特点,简单.快速.高效.本次我们通过C#以及Xaml来做移动开发. 1.开发工具visual studio 2015或visual studio 2017.当然visual studio 20 ...
- samba服务:为在windows下操作linux的文件而生
vi/vim编辑器好玩吗?虽有着层出不穷的语法糖但又如何与传统的sublime相媲美? 那么,来吧~ 动手跟我一起做个samba服务吧~ 安装 yum -y install samba 配置 ...