同步调用
被调用驱动程序代码:
typedef struct _DEVICE_EXTENSION{
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
//自己的扩展
PIRP currentPendingIRP;
KDPC dpc;
KTIMER timer;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
#include<ntddk.h>
#include<ntstatus.h>
#include "Driver.h"
#define DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLICLINE_NAME L"\\??\\MyTestDriver" //ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"//相当于起的别名
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
PVOID pDeviceExtension = 0;
//实现卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pNextObj->DeviceExtension;
//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
DbgPrint("删除设备\n");
}
}
NTSTATUS IrpDefaultProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
//将IRP设置为挂起
IoMarkIrpPending(pIrp);
//设置一个DPC 如果超时就在DPC的回调处理中把IRP取消掉。
PDEVICE_EXTENSION pDevExt = pDeviceObject->DeviceExtension;
//保存IRP指针
pDevExt->currentPendingIRP = pIrp;
//定义3s的超时
ULONG ulMicroSecond = 3000000;
//将32位整数转换成64位整数
LARGE_INTEGER timeOut = RtlConvertLongToLargeInteger(-10 * ulMicroSecond);
//开启计时器
KeSetTimer(&pDevExt->timer, timeOut, &pDevExt->dpc);
pIrp->IoStatus.Status = STATUS_PENDING;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
return STATUS_PENDING;
}
#pragma LOCKEDCODE
void DpcRoutine(
PKDPC pDpc,
PVOID Context,
PVOID Arg1,
PVOID Arg2
)
{
//获得设备扩展的指针
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)Context;
//获得挂起的IRP
PIRP pIrp = pDevExt->currentPendingIRP;
//设置完成状态为STATUS_CANCELLED
pIrp->IoStatus.Status = STATUS_CANCELLED;
//设置操作的字节数
pIrp->IoStatus.Information = 0;
DbgPrint("Delay IRP run\n");
//完成请求
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING DeviceName;
UNICODE_STRING SymbolicLinkName;
//Irp默认处理
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_WRITE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_READ] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLEANUP] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SET_INFORMATION] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SHUTDOWN] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IrpDefaultProc;
//设置派遣函数和卸载函数
pDriver->DriverUnload = DriverUnload;
pDriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pDriver,
sizeof(DEVICE_EXTENSION), //扩展设备大小
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n", status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
PDEVICE_EXTENSION pDevExt = pDeviceObj->DeviceExtension;
pDevExt->pDevice = pDeviceObj;
pDevExt->ustrDeviceName = DeviceName;
pDevExt->ustrSymLinkName = SymbolicLinkName;
/*测试代码*/
//初始化定时器对象
KeInitializeTimer(&pDevExt->timer);
//初始化DPC对象
KeInitializeDpc(&pDevExt->dpc, DpcRoutine, pDevExt);
/*测试代码*/
return STATUS_SUCCESS;
}
}
调用者代码:
#include<ntddk.h>
#include<ntstatus.h>
#include "Driver.h"
#define DEVICE_NAME L"\\Device\\dale"
#define CALLED_DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLICLINE_NAME L"\\??\\dale_symbol" //ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"//相当于起的别名
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
PVOID pDeviceExtension = 0;
//实现卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pNextObj->DeviceExtension;
//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
DbgPrint("删除设备\n");
}
}
NTSTATUS IrpDefaultProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, CALLED_DEVICE_NAME);
//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//同步打开设备
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&objectAttributes,
&status_block,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (NT_SUCCESS(ntStatus))
{
ZwReadFile(hDevice, NULL, NULL, NULL, &status_block, NULL, 0, NULL, NULL);
}
ZwClose(hDevice);
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING DeviceName;
UNICODE_STRING SymbolicLinkName;
//Irp默认处理
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_WRITE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_READ] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLEANUP] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SET_INFORMATION] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SHUTDOWN] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IrpDefaultProc;
//设置派遣函数和卸载函数
pDriver->DriverUnload = DriverUnload;
pDriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pDriver,
sizeof(DEVICE_EXTENSION), //扩展设备大小
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n", status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
PDEVICE_EXTENSION pDevExt = pDeviceObj->DeviceExtension;
pDevExt->pDevice = pDeviceObj;
pDevExt->ustrDeviceName = DeviceName;
pDevExt->ustrSymLinkName = SymbolicLinkName;
/*测试代码*/
/*测试代码*/
return STATUS_SUCCESS;
}
3环测试代码
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hDevice =
CreateFile(L"\\\\.\\dale_symbol",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,//使用同步方式
NULL );
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Open Device failed!");
getchar();
return 1;
}
char buffer[20]={0};
OVERLAPPED OverLapped={0};
ReadFile(hDevice,buffer,10,0,&OverLapped);
//创建IRP_MJ_CLEANUP IRP
CloseHandle(hDevice);
return 0;
}
运行流程: 3环ReadFile -》调用驱动B 的Read IRP处理 -》Read IRP调用CreateFile 同步打开被调用驱动A 调用 ReadFile 发送IRP -》被调用驱动A Read IRP 挂起IRP 设置DPC -》DPC IRP超时处理
异步调用
被调用驱动代码不变
#include<ntddk.h>
#include<ntstatus.h>
#include "Driver.h"
#define DEVICE_NAME L"\\Device\\dale"
#define CALLED_DEVICE_NAME L"\\Device\\MyDevice"
#define SYMBOLICLINE_NAME L"\\??\\dale_symbol" //ring3用CreateFile打开设备时,用"\\\\.\\MyTestDriver"//相当于起的别名
#define OPER1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define OPER2 CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
PVOID pDeviceExtension = 0;
//实现卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pNextObj->DeviceExtension;
//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
DbgPrint("删除设备\n");
}
}
NTSTATUS IrpDefaultProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;//getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;//返回给3环多少数据,没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
VOID CompleteDriverA_Read(PVOID context, PIO_STATUS_BLOCK pStatus_block, ULONG Reserved)
{
KdPrint(("DriverB:The Driver A Read completed now!\n"));
KeSetEvent((PKEVENT)context, IO_NO_INCREMENT, FALSE);
}
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, CALLED_DEVICE_NAME);
//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//异步打开设备
//没有设定了FILE_SYNCHRONOUS_IO_NONALERT和FILE_SYNCHRONOUS_IO_ALERT为异步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE
&objectAttributes,
&status_block,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN_IF, 0, NULL, 0);
KEVENT event;
//初始化事件,用于异步读
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwReadFile(hDevice, NULL, CompleteDriverA_Read, &event, &status_block, NULL, 0, &offset, NULL);
}
if (ntStatus == STATUS_PENDING)
{
KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n"));
KdPrint(("DriverB:Waiting..."));
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
}
ZwClose(hDevice);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
NTSTATUS status = 0;
ULONG uIndex = 0;
PDEVICE_OBJECT pDeviceObj = NULL;
UNICODE_STRING DeviceName;
UNICODE_STRING SymbolicLinkName;
//Irp默认处理
pDriver->MajorFunction[IRP_MJ_CREATE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLOSE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_WRITE] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_READ] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_CLEANUP] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SET_INFORMATION] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SHUTDOWN] = IrpDefaultProc;
pDriver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = IrpDefaultProc;
//设置派遣函数和卸载函数
pDriver->DriverUnload = DriverUnload;
pDriver->MajorFunction[IRP_MJ_READ] = IrpReadProc;
//创建设备名称
RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
//创建设备 让三环的API能够找到,才能实现通信
status = IoCreateDevice(pDriver,
sizeof(DEVICE_EXTENSION), //扩展设备大小
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pDeviceObj);
if (status != STATUS_SUCCESS)
{
DbgPrint("创建设备失败! status=%x\r\n", status);
return status;
}
//设置交互数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
//创建符号链接名称,就是给该设备在三环起个能用的别名
RtlInitUnicodeString(&SymbolicLinkName, SYMBOLICLINE_NAME);
//创建符号链接
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
DbgPrint("创建符号链接失败!\r\n");
IoDeleteDevice(pDeviceObj);
return status;
}
PDEVICE_EXTENSION pDevExt = pDeviceObj->DeviceExtension;
pDevExt->pDevice = pDeviceObj;
pDevExt->ustrDeviceName = DeviceName;
pDevExt->ustrSymLinkName = SymbolicLinkName;
/*测试代码*/
/*测试代码*/
return STATUS_SUCCESS;
}
3环代码
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
int main()
{
//打开指定设备 加上FILE_FLAG_OVERLAPPED标志 表示设备操作为异步的
HANDLE hDevice=CreateFileW( L"\\\\.\\dale_symbol",
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED ,//FILE_FLAG_OVERLAPPED表示使用异步打开
0
);
if (hDevice==INVALID_HANDLE_VALUE)
{
printf("设备打开失败\n");
getchar();
return false;
}
OVERLAPPED Overlap={0};
Overlap.hEvent=CreateEvent(0,0,0,0);
UCHAR buffer[100];
DWORD dwRead;
ReadFile(hDevice,buffer,100,&dwRead,&Overlap);
//使用异步操作,程序不会等待ReadFile执行完,而是继续往下执行
printf("并行执行");
WaitForSingleObject(Overlap.hEvent,INFINITE);
CloseHandle(hDevice);
getchar();
return 0;
}
总结:
异步方式调用驱动在ZwReadFile里面传入了一个APC函数,当IRP_MJ_READ在被调用驱动A处理结束后(驱动B ReadFile读取A时产生的IRP)会调用这个APC函数。
异步调用方式二
每打开一个设备,都会存在一个关联的文件对象(FILE_OBJECT),利用内核函数ObReferenceObjectByHandle可以获得和设备相关的文件对象指针。
当IRP_MJ_READ请求被结束后,文件对象的Event会被设置。因此可以利用文件对象的Event来当做同步点使用。
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDeviceA");
//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//异步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE
&objectAttributes,
&status_block,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN_IF, 0, NULL, 0);
LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0);
if (NT_SUCCESS(ntStatus))
{
ntStatus = ZwReadFile(hDevice, NULL, NULL, NULL, &status_block, NULL, 0, &offset, NULL);
}
if (ntStatus == STATUS_PENDING)
{
KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n"));
PFILE_OBJECT FileObject;
ntStatus = ObReferenceObjectByHandle(hDevice,
0, //源代码中为什么使用EVENT_MODIFY_STATE这个值?
*IoFileObjectType,
KernelMode,
(PVOID*)&FileObject,
NULL);
if (NT_SUCCESS(ntStatus))
{
KdPrint(("DriverB:Waiting..."));
KeWaitForSingleObject(&FileObject->Event, Executive, KernelMode, FALSE, NULL);
KdPrint(("DriverB:Driver A Read IRP completed now!\n"));
ObDereferenceObject(FileObject);
}
}
ZwClose(hDevice);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
通过符号链接打开设备
利用ZwOpenSymbolicLinkObject 内核函数先得到符号链接的句柄,然后使用
ZwQuerySymbolicLinkObject内核函数查找到设备名。通过设备名就可以方便地打开设
备了。
NTSYSAPI NTSTATUS ZwOpenSymbolicLinkObject(
PHANDLE LinkHandle, //打开的符号链接句柄
ACCESS_MASK DesiredAccess, //访问掩码 FILE_ALL_ACCESS
POBJECT_ATTRIBUTES ObjectAttributes //对象属性,符号链接名在对象里
);
NTSYSAPI NTSTATUS ZwQuerySymbolicLinkObject(
IN HANDLE LinkHandle, //ZwOpenSymbolicLinkObject成功打开的句柄
OUT PUNICODE_STRING LinkTarget, //设备名
OUT PULONG ReturnedLength //如果调用失败,该值指示了LinkTarget的最大长度
);
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceSymbolicLinkName;
RtlInitUnicodeString(&DeviceSymbolicLinkName, CALLED_SYMBOLICLINE_NAME); //被调用驱动A的符号连接名
//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceSymbolicLinkName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
HANDLE hSymbolic;
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwOpenSymbolicLinkObject(&hSymbolic, FILE_ALL_ACCESS, &objectAttributes);
#define UNICODE_SIZE 50
UNICODE_STRING LinkTarget;
LinkTarget.Buffer = (PWSTR)ExAllocatePool(PagedPool, UNICODE_SIZE);
LinkTarget.Length = 0;
LinkTarget.MaximumLength = UNICODE_SIZE;
ULONG unicode_length;
ntStatus = ZwQuerySymbolicLinkObject(hSymbolic, &LinkTarget, &unicode_length);
KdPrint(("DriverB:The device name is %wZ\n", &LinkTarget));
InitializeObjectAttributes(&objectAttributes,
&LinkTarget,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&objectAttributes,
&status_block,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(ntStatus))
{
ZwClose(hSymbolic);
ExFreePool(LinkTarget.Buffer);
return ntStatus;
}
ZwReadFile(hDevice, NULL, NULL, NULL, &status_block, NULL, 0, NULL, NULL);
ZwClose(hDevice);
ZwClose(hSymbolic);
ExFreePool(LinkTarget.Buffer);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
通过设备指针调用其他驱动程序
用loBuildSynchronousFsdRequest创建IRP
NTSTATUS IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName, //设备名,用UNICODE字符串表示
IN ACCESS_MASK DesiredAccess, //以什么样的权限得到设备指针
OUT PFILE_OBJECT *FileObject, //返回一个和设备相关的文件对象指针
OUT PDEVICE_OBJECT *DeviceObject //返回设备对象指针
);
可以通过loBuildSynchronousFsdRequest和loBuildAsynchronousFsdRcquest 两个内核
函数创建IRP,它们分别用来创建同步类型的IRP和创建异步类型的IRP。这两个内核函
数可以创建IRP_ MJ_ PNP、IRP_ MJ_ READ、IRP_ MJ_ WRITE、IRP_ MJ_ FLUSH_ BUFFERS和IRP_ MJ_ SHUTDOWN类型的IRP。
另外,还可以使用loAllocateIrp 内核函数,它可以创建任意类型的IRP。
IoBuildSynchronousFsdRequest、loBuildAsynchronousFsdRequest 、loBuildDeviceloControlRequest这三个内核函数是属于靠近上层的内核函数。而loAllocatelrp 是比较底层的内核函数,以上三个内核函数都是通过调用loAllocateIrp实现的。
创建完IRP后,还要构造IRP的I/O堆栈,每层I/O堆栈对应一个设备对象。由于示
例程序DriverA是单层驱动程序,所以只需要构造IRP的第一层I/O堆栈。
最后是通过loCallDriver内核函数调用相应的驱动。loCallDriver 内核函数会根据IRP
的类型,找到相应的派遣函数。
总结一下,手动创建IRP有以下几个步骤。
➊先得到设备的指针。一种 方法是用IoGetDeviceObjectPointer 内核函数得到设备对
象指针。另外一种方法是用ZwCreateFile内核函数先得到设备句柄,然后调用
ObReferenceObjectByPointer内核函数通过设备句柄得到设备对象指针。
❷手动创建IRP, 有4个内核函数可以选择,它们分别是loBuildSynchronous
FsdRequest、loBuildAsynchronousFsdRequest、 loBuildDeviceloControlRequest 和IoAllocatelrp。
其中IoAllocateIrp 内核函数是最灵活的,使用也最复杂。
❸构造IRP的IO堆栈。
④调用loCallDriver内核函数,其内部会调用设备对象的派遣函数。
- 用IoBuildSynchronousFsdRequest创建IRP
PIRP IoBuildSynchronousFsdRequest(
ULONG MajorFunction, //第一个参数MajorFunction:这个参数是创建的IRP的主类型,loBuildSynchronous
//FsdRequest函数只支持IRP_MJ_PNP、IRP_MJ_READ、IRP_MJ_WRITE、
//IRP_MJ_FLUSH_BUFFERS和IRP_MJ_SHUTDOWN。
PDEVICE_OBJECT DeviceObject, //设备对象指针 IRP将会传递给这个设备指针
PVOID Buffer, //对于IRP_MJ_READ和IRP_MJ_WRITE buffer指的是输入和输出缓冲区
ULONG Length, //缓冲区的大小
PLARGE_INTEGER StartingOffset, //指向磁盘上偏移量的指针,用于读取和写入请求。这个值的单位和意义是特定于驱动程序的.此参数是读写请求所必需的,但对于刷新和关闭请求,则必须为零
PKEVENT Event,//同步事件,这是创建同步类型IRP的关键
PIO_STATUS_BLOCK IoStatusBlock //操作状态
);
使用loBuildSynchronousFsdRequest内核函数创建同步类型IRP,关键在于第六个参数
Event。在调用loBuildSynchronousFsdRequest之前,需要准备一“个事件。 这个事件会和IRP
请求进行关联,当IRP请求被结束时该事件被触发。loBuildSynchronousFsdRequest 和
loBuildAsynchronousFsdRequest内核函数之间的区别就是是否提供事件
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, CALLED_DEVICE_NAME);
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
//第一步 得到设备对象指针,计数器加1
//如果是第一次调用IoGetDeviceObjectPointer,会打开设备,相当于调用ZwCreateFile
ntStatus = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &FileObject, &DeviceObject);
KdPrint(("DriverB:FileObject:%x\n", FileObject));
KdPrint(("DriverB:DeviceObject:%x\n", DeviceObject));
if (!NT_SUCCESS(ntStatus))
{
KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus));
ntStatus = STATUS_UNSUCCESSFUL;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
//初始化一个事件对象
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IO_STATUS_BLOCK status_block;
LARGE_INTEGER offsert = RtlConvertLongToLargeInteger(0);
//创建同步IRP
PIRP pNewIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
NULL, 0,
&offsert, &event, &status_block);
KdPrint(("DriverB:pNewIrp:%x\n", pNewIrp));
//构造IRP的IO堆栈 任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:
//数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
stack->FileObject = FileObject;
//调用DriverA,会一直调用到DriverA的派遣函数
NTSTATUS status = IoCallDriver(DeviceObject, pNewIrp);
if (status == STATUS_PENDING) {
//如果DriverA的派遣函数没有完成IRP,则等待IRP完成
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
status = status_block.Status;
}
//将引用计数减1,如果此时计数器减为0,
//则将关闭设备,相当于调用ZwClose
ObDereferenceObject(FileObject);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
用loBuildAsynchronousFsdRequest创建IRP
这个函数没有Event对象,如果需要等待创建的IRP处理完成的话,可以利用IRP里面的UserEvent来进行同步.
当执行loCompleteRequest内核函数时,操作系统会检查IRP的UserEvent子域是否为
空。如果UserEvent子域非空,则它代表一个事件指针,这时候loCompleteRequest会设置
这个事件。
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDeviceA");
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
//得到设备对象指针
ntStatus = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &FileObject, &DeviceObject);
KdPrint(("DriverB:FileObject:%x\n", FileObject));
KdPrint(("DriverB:DeviceObject:%x\n", DeviceObject));
if (!NT_SUCCESS(ntStatus))
{
KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus));
ntStatus = STATUS_UNSUCCESSFUL;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IO_STATUS_BLOCK status_block;
LARGE_INTEGER offsert = RtlConvertLongToLargeInteger(0);
//创建异步IRP
PIRP pNewIrp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
DeviceObject,
NULL, 0,
&offsert, &status_block);
KdPrint(("pNewIrp->UserEvent :%x\n", pNewIrp->UserEvent));
//设置pNewIrp->UserEvent,这样在IRP完成后可以通知该事件
pNewIrp->UserEvent = &event;
KdPrint(("DriverB:pNewIrp:%x\n", pNewIrp));
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
stack->FileObject = FileObject;
NTSTATUS status = IoCallDriver(DeviceObject, pNewIrp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
status = status_block.Status;
}
ZwClose(FileObject);
//关闭设备句柄
ObDereferenceObject(FileObject);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
用loAllocatelrp创建IRP
所有对设备的操作都会转化为-一个IRP,IRP会在驱动程序中被处理。IRP 请求被结
束,就代表着对设备操作的结束,IRP 的完成状态就是操作的完成状态。而所有的IRP最
终都是由IoAllocateIrp内核函数创建的。
整个Windows是个异步的框架,IRP 请求可以被异步地结束。如果对异步请求用同步
事件进行同步时,异步请求就变成了同步请求。可以说同步请求是异步请求的一个特例,
这如同静止状态是运动状态的一一个特例一样。 驱动程序开发者大部分的任务就是创建和处
理IRP请求。
PIRP
IoAllocateIrp (
IN CCHAR Stacksize //设置堆栈大小
IN BOOLEAN ChargeQuota //是否使用磁盘配额
使用loAllocatelrp来创建IRP时,需要自己手动调用IoFreeIrp来删除IRP对象.
#pragma PAGEDCODE
NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDDKDeviceA");
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
//得到设备对象指针
ntStatus = IoGetDeviceObjectPointer(&DeviceName, FILE_ALL_ACCESS, &FileObject, &DeviceObject);
KdPrint(("DriverB:FileObject:%x\n", FileObject));
KdPrint(("DriverB:DeviceObject:%x\n", DeviceObject));
if (!NT_SUCCESS(ntStatus))
{
KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus));
ntStatus = STATUS_UNSUCCESSFUL;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
PIRP pNewIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
KdPrint(("pNewIrp->UserEvent :%x\n", pNewIrp->UserEvent));
pNewIrp->UserEvent = &event;
IO_STATUS_BLOCK status_block;
pNewIrp->UserIosb = &status_block;
pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread();
//因为DriverA是BUFFER IO设备
pNewIrp->AssociatedIrp.SystemBuffer = NULL;
KdPrint(("DriverB:pNewIrp:%x\n", pNewIrp));
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
stack->MajorFunction = IRP_MJ_READ;
stack->MinorFunction = IRP_MN_NORMAL;//0
stack->FileObject = FileObject;
//调用DriverA驱动
NTSTATUS status = IoCallDriver(DeviceObject, pNewIrp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event,
Executive,
KernelMode,
FALSE, // Not alertable
NULL);
KdPrint(("STATUS_PENDING\n"));
}
//对象引用计数-1
ObDereferenceObject(FileObject);
//释放IRP对象
IoFreeIrp(pNewIrp);
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
其它方式获得设备指针
使用未导出函数ObReferenceObjectByName来获得设备指针
NTKERNELAPI NTSTATUS
ObReferenceObjectByName(
IN PUNICODE_STRING objectName, //指定打开设备的设备名
IN ULONG Attributes, //这个属性一般设置为OBJ_CASE_INSENSITIVE
IN PACCESS_STATE PassedAccessstate OPTIONAL, //一般设置为NULL
IN ACCESS_MASK DesiredAcces OPTIONAL, //一般设置为FILE_ALL_ACCESS
IN POBJECT_TYPE ObjectType, //如果要获得设备对象指针,需要指定这个参数为IoDeviceObjectType
IN KPROCESSOR_MODE AccessMode, //驱动中用KernelMode
IN OUT PVOID ParseContext OPTIONAL, //一般设置为NULL
OUT PVOID *object //返回的内核对象的指针
);
extern POBJECT_TYPE IoDeviceObjectType;
ObReferenceObjectByName和IoGetDeviceObjectPointer内核函数虽然都是获取设备对
象指针,但是却有一些不同。 ObReferenceObjectByName 内核函数仅仅是通过名字得到对象的指针而已,除了得到设备对象指针外,也可以得到别的内核对象的指针,比如内核事件、互斥体对象等。ObReferenceObjectByName 内核函数会增加对设备对象的引用计数,因此在需要使用设备对象完毕后,需要调用ObDereferenceObject内核函数将引用计数减1。
而loGetDeviceObjectPointer 内核函数只能得到设备对象指针。在loGetDeviceObject
Pointer内核函数内部,得到设备对象指针后还会打开设备对象,也就是相当于向该设备对象发送IRP_ MJ CREATE的IRP。同时,loGetDeviceObjectPointer 内核函数还获得了与设备对象相关的文件对象句柄。在使用完设备对象后,需要对设备相关的文件对象调用
ObDereferenceObject内核函数,这样相当于向设备发送IRP MJ CLOSE。
#pragma PAGEDCODE
NTSTATUS IrpReadProc(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
RtlInitUnicodeString( &DeviceName, L"\\??\\HelloDDKA" );
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
ntStatus = ObReferenceObjectByName(&DeviceName,OBJ_CASE_INSENSITIVE,NULL,FILE_ALL_ACCESS,IoDeviceObjectType,KernelMode,NULL,(PVOID*)&DeviceObject);
KdPrint(("ntStatus %x\n",ntStatus));
KdPrint(("DeviceObject %x\n",DeviceObject));
ObDereferenceObject( FileObject );
ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
剖析loGetDeviceObjectPointer
//模拟IoGetDeviceObjectPointer实现
NTSTATUS
MyIoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT *FileObject,
OUT PDEVICE_OBJECT *DeviceObject
)
{
PFILE_OBJECT fileObject;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
//设置要打开的设备的设备名
InitializeObjectAttributes( &objectAttributes,
ObjectName,
OBJ_KERNEL_HANDLE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//ZwOpenFile打开设备
status = ZwOpenFile( &fileHandle,
DesiredAccess,
&objectAttributes,
&ioStatus,
0,
FILE_NON_DIRECTORY_FILE );
if (NT_SUCCESS( status ))
{
//得到文件对象指针
status = ObReferenceObjectByHandle( fileHandle,
0,
*IoFileObjectType,
KernelMode,
(PVOID *) &fileObject,
NULL );
if (NT_SUCCESS( status ))
{
*FileObject = fileObject;
//得到设备对象指针
*DeviceObject = IoGetBaseFileSystemDeviceObject( fileObject );
}
ZwClose( fileHandle );
}
return status;
}