#include <ntddk.h>
const WCHAR DeviceNameBuffer[] = L"\\Device\\MyHelloWorldDriver"; //设备用于内核空间的名称
const WCHAR DeviceLinkBuffer[] = L"\\DosDevices\\MyHelloWorldDriver"; //用于用户层名字空间的名称,用户程序将
//通过此名称与驱动通信
PDEVICE_OBJECT g_pDeviceObject = NULL;
//全局计数
ULONG g_GlobalCounter = 0;
//访问全局计数的自旋锁
KSPIN_LOCK g_SpinLock;
//内核事件对象,用于通知 计数增加线程 计数减少线程已经启动了
KEVENT g_Event;
VOID IncreaseCounterThread( PVOID pContext )
{
KIRQL dwOrqlLevel;
ULONG ulIncreaseCount = 20;
DbgPrint( "Enter IncreaseCounterThread\n" );
//等待计数减少线程启动
KeWaitForSingleObject( &g_Event, Executive, KernelMode, 0, 0 );
//Get the spin lock before increase counter
KeAcquireSpinLock( &g_SpinLock, &dwOrqlLevel );
while( ulIncreaseCount > 0 )
{
ulIncreaseCount--;
g_GlobalCounter++;
DbgPrint( "Global counter increase to %d\n", g_GlobalCounter );
}
//Release spin lock
KeReleaseSpinLock( &g_SpinLock, dwOrqlLevel );
//内核线程的结束需要现成自己调用PsTerminateSystemThread来完成它
PsTerminateSystemThread( STATUS_SUCCESS );
}
VOID DecreaseCounterThread( PVOID pContext )
{
KIRQL dwOrqlLevel;
ULONG ulDecreaseCount = 20;
DbgPrint( "Enter DecreaseCounterThread\n" );
//通知计数增加进程,我启动了
KeSetEvent( &g_Event , IO_NO_INCREMENT, FALSE );
//Get the spin lock before increase counter
KeAcquireSpinLock( &g_SpinLock, &dwOrqlLevel );
while( ulDecreaseCount > 0 )
{
ulDecreaseCount--;
g_GlobalCounter--;
DbgPrint( "Global counter decrease to %d\n", g_GlobalCounter );
}
//Release spin lock
KeReleaseSpinLock( &g_SpinLock, dwOrqlLevel );
//内核线程的结束需要现成自己调用PsTerminateSystemThread来完成它
PsTerminateSystemThread( STATUS_SUCCESS );
}
NTSTATUS RegisterDeviceLink()
{
NTSTATUS ntStatus;
UNICODE_STRING unicodeDeviceLink;
UNICODE_STRING unicodeDeviceName;
RtlInitUnicodeString(&unicodeDeviceLink, DeviceLinkBuffer);
RtlInitUnicodeString(&unicodeDeviceName, DeviceNameBuffer);
ntStatus = IoCreateSymbolicLink(&unicodeDeviceLink, &unicodeDeviceName);
return ntStatus;
}
NTSTATUS RegisterDriver(PDRIVER_OBJECT pDriverObject)
{
NTSTATUS ntStatus;
UNICODE_STRING unicodeDeviceName;
PDEVICE_OBJECT pDeviceObject;
RtlInitUnicodeString(&unicodeDeviceName, DeviceNameBuffer);
ntStatus = IoCreateDevice //IoCreateDevice是系统提供的API,用于创建一个DEVICE_OBJECTD对象
(
pDriverObject, //第一个参数是DRIVER_OBJECT指针,该对象是由系统生成,并通过DriverEntry传递给我们的
0, //DEVICE_EXTENSION, 囫囵吞枣原则,大家暂时先忽略
&unicodeDeviceName, //设备名称
FILE_DEVICE_UNKNOWN, //设备的类型,这里设置成该值即可
0, //设备特征,取0即可
FALSE, //必须设为false
&pDeviceObject //pDeviceObject 将指向生成的DEVICE_OBJECT对象
);
//设置缓冲区方式读写
pDeviceObject->Flags |= DO_BUFFERED_IO;
//将生成的DEVICE_OBJECT对象寄存到全局指针,驱动卸载的时候将它删除
g_pDeviceObject = pDeviceObject;
return ntStatus; //如果ntStatus 不等于STATUS_SUCCESS的话,表明函数调用失败。
//记住驱动编程一定要检测函数调用的返回值
}
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDeviceObject, PIRP pIRP)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
DbgPrint("Enter DispatchCreate\n");
pIRP->IoStatus.Status = STATUS_SUCCESS;
pIRP->IoStatus.Information = 0;
IoCompleteRequest(pIRP, IO_NO_INCREMENT); //通知系统连接准备工作已经完成
DbgPrint("Leave DispatchCreate\n");
return ntStatus;
}
PIRP g_pReadFileIRP = NULL;
KDPC g_DpcObject;
KTIMER g_TimerObject;
//定时器触发例程
VOID DpcTimerProcessRoutine( PKDPC pDpc, PVOID pContext, PVOID SysArg1, PVOID SysArg2 )
{
//在这里我们完成在DispatchRead中挂起的IRP
PIO_STACK_LOCATION pStackLocation = IoGetCurrentIrpStackLocation( g_pReadFileIRP );
//获取缓冲区长度
ULONG ulReadLength = pStackLocation->Parameters.Read.Length;
DbgPrint( "Enter DpcTimerProcessRoutine length is :%d\n", ulReadLength );
//填充缓冲区
memset( g_pReadFileIRP->AssociatedIrp.SystemBuffer, 0xAA, ulReadLength );
//通知系统IRP所代表的请求已经满足
g_pReadFileIRP->IoStatus.Status = STATUS_SUCCESS;
g_pReadFileIRP->IoStatus.Information = ulReadLength;
IoCompleteRequest( g_pReadFileIRP, IO_NO_INCREMENT );
DbgPrint( "Leave DpcTimerProcessRoutine\n" );
}
/*
当用户程序通过ReadFile向驱动请求读数据时,该函数将被系统调用
*/
NTSTATUS DispatchRead(PDEVICE_OBJECT pDeviceObject, PIRP pIRP)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
LONG delayTimer = 0;
LARGE_INTEGER delayTimer64 = {0};
DbgPrint("Enter DispatchRead\n");
//由于我们在这里不直接完成读取操作因此调用IoMarkIrpPending,这样系统就知道这次读请求将被挂起
IoMarkIrpPending( pIRP );
// 先记录下当前的IRP
g_pReadFileIRP = pIRP;
//初始化一个定时器,当定时器被触发后,完成在这里被挂起的IRP
KeInitializeTimer( &g_TimerObject );
//指定定时器触发时的回调函数及传入参数
KeInitializeDpc( &g_DpcObject, DpcTimerProcessRoutine, (PVOID)pDeviceObject );
//设置定时器出发时间,由于定时器时间单位是 100纳秒,所以要设置间隔为3秒(3秒 = 3*1000*1000*10*100ns)的话,那么定时器的延时为 3*1000*1000*10
delayTimer = -6*1000*1000*10;
//将32位证书转换为64为整数
delayTimer64.QuadPart = delayTimer;
//启动定时器
KeSetTimer( &g_TimerObject, delayTimer64, &g_DpcObject );
DbgPrint("Leave DispatchRead\n");
//如果请求操作被推迟的话,就必须返回STATUS_PENDING
return STATUS_PENDING;
}
/*
当系统卸载驱动时该函数会被调用
*/
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING unicodeSymbolicLink;
//驱动卸载时应回收动态分配的内存,调用IoDeleteSymbolicLink注销驱动在用户空间注册的名字
//以及调用IoDeleteDevice删除设备对象
RtlInitUnicodeString(&unicodeSymbolicLink, DeviceLinkBuffer);
IoDeleteSymbolicLink(&unicodeSymbolicLink);
IoDeleteDevice(g_pDeviceObject);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pwstrRegistry)
{
NTSTATUS ntStatus;
HANDLE ThreadHandleIncrease, ThreadHandleDecrease;
LARGE_INTEGER interval;
DbgPrint("Hello World My Driver!");
//注册驱动卸载函数
pDriverObject->DriverUnload = DriverUnload;
//导出DispatchCreate给系统调用,当用户程序通过CreateFile打开驱动时,
//系统将调用此导出函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
//将DispatchRead导出,当用户调用ReadFile时他就可被系统调用
pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
ntStatus = RegisterDriver( pDriverObject );
ntStatus = RegisterDeviceLink();
//初始化自旋锁
KeInitializeSpinLock( &g_SpinLock );
//初始化内核事件对象
KeInitializeEvent( &g_Event, SynchronizationEvent, FALSE );
//启动两个线程,一个线程将全局计数增加,另个减少全局计数
//在驱动程序中需要调用PsCreateSystemThread来启动线程,
/*
PsCreateSystemThread( PHANDLE ThreadHandle, ULONG DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle,
PKSTART_ROUTINE StartRoutine, PVOID StartContext )
*/
//该API输入参数较多
//我们只关心第一个用于返回线程的句柄, 第5个是线程函数,第六个是输入给线程函数的参数
PsCreateSystemThread( &ThreadHandleIncrease, 0, NULL, NULL, NULL,
IncreaseCounterThread, NULL );
//延迟30微秒,确保增加计数线程先启动
interval.QuadPart = -300;
KeDelayExecutionThread( KernelMode, FALSE, &interval );
PsCreateSystemThread( &ThreadHandleIncrease, 0, NULL, NULL, NULL,
DecreaseCounterThread, NULL );
return ntStatus;
}