您的位置:首页 > 其它

WDM驱动程序入门-Hello WDM

2013-05-30 20:09 337 查看
/一定要的头文件,声明了函数模块和变量:

#include "HelloWDM.h"

/***************************************************************

函数名称:DriverEntry()

功能描述:WDM程序入口

***************************************************************/

//extern "C"是必须的,表示“用C链接”。如果你的文件名是HelloWDM.c的话,这句可以省略。

extern "C"

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath)

{

//指定“添加设备”消息由函数“HelloWDMAddDevice()”来处理:

DriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;

//指定“即插即用”消息由函数“HelloWDMPnp()”来处理:

DriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

//返回一个NTSTATUS值STATUS_SUCCESS。几乎所有的驱动程序例程都必须返回一个NTSTATUS值,这些值在NTSTATUS.H DDK头文件中有详细的定义。

return STATUS_SUCCESS;

}

/***************************************************************

函数名称:HelloWDMAddDevice()

功能描述:处理“添加设备”消息

***************************************************************/

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,

IN PDEVICE_OBJECT PhysicalDeviceObject)

{

//定义一个NTSTATUS类型的返回值:

NTSTATUS status;

//定义一个功能设备对象(Functional Device Object):

PDEVICE_OBJECT fdo;

//创建我们的功能设备对象,并储存到fdo中:

status = IoCreateDevice(

DriverObject, //驱动程序对象

sizeof(DEVICE_EXTENSION), //要求的设备扩展的大小

NULL, //设备名称,这里为NULL

FILE_DEVICE_UNKNOWN, //设备的类型,在标准头文件WDM.H或NTDDK.H中列出的FILE_DEVICE_xxx值之一

0, //各种常量用OR组合在一起,指示可删除介质、只读等。

FALSE, //如果一次只有一个线程可以访问该设备,为TRUE,否则为FALSE

&fdo); //返回的设备对象

//NT_SUCCESS宏用于测试IoCreateDevice内核是否成功完成。不要忘记检查对内核的所有调用是否成功。NT_ERROR宏不等同于!NT_SUCCESS,最好使用!NT_SUCCESS,因为除了错误外,它还截获警告信息。

if( !NT_SUCCESS(status))

return status;

//创建一个设备扩展对象dx,用于存储指向fdo的指针:

PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;

dx->fdo = fdo;

//用IoAttachDeviceToDeviceStack函数把HelloWDM设备挂接到设备栈:

dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);

//设置fdo的flags。有两个“位”是必须改变的,一个是必须清除DO_DEVICE_INITIALIZING标志,如果在DriverEntry例程中调用IoCreateDevice(),就不需要清除这个标志位。还有一个是必须设置DO_BUFFER_IO标志位:

fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;

fdo->Flags &= ~DO_DEVICE_INITIALIZING;

//返回值:

return STATUS_SUCCESS;

}

/***************************************************************

函数名称:HelloWDMPnp()

功能描述:处理“即插即用”消息

***************************************************************/

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,

IN PIRP Irp)

{

//创建一个设备扩展对象dx,用于存储指向fdo的指针:

PDEVICE_EXTENSION dx=(PDEVICE_EXTENSION)fdo->DeviceExtension;

//首先要通过函数IoGetCurrentIrpStackLocation()得到当前的IRP,并由此得到Minor Function:

PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);

ULONG MinorFunction = IrpStack->MinorFunction;

//然后把这个Minor Function传递给下一个设备栈:

IoSkipCurrentIrpStackLocation(Irp);

NTSTATUS status = IoCallDriver( dx->NextStackDevice, Irp);

//处理“即插即用”次功能代码:

//当Minor Function等于IRP_MN_REMOVE_DEVICE时,说明有设备被拔出或卸下,这时要取消资源分配并删除设备:

if( MinorFunction==IRP_MN_REMOVE_DEVICE)

{

//取消设备接口:

IoSetDeviceInterfaceState(&dx->ifSymLinkName, FALSE);

RtlFreeUnicodeString(&dx->ifSymLinkName);

//调用IoDetachDevice()把fdo从设备栈中脱开:

if (dx->NextStackDevice)

IoDetachDevice(dx->NextStackDevice);

//删除fdo:

IoDeleteDevice(fdo);

}

//返回值:

return status;

}

/***************************************************************

程序名称:Hello World for WDM

文件名称:HelloWDM.h

作者:罗聪

日期:2002-8-16

***************************************************************/

//头文件,只是声明一些函数和变量,比较简单就不多说了,请读者自行研究:

#ifdef __cplusplus

extern "C"

{

#endif

#include "ntddk.h"

#ifdef __cplusplus

}

#endif

typedef struct _DEVICE_EXTENSION

{

PDEVICE_OBJECT fdo;

PDEVICE_OBJECT NextStackDevice;

UNICODE_STRING ifSymLinkName;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,

IN PDEVICE_OBJECT PhysicalDeviceObject);

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,

IN PIRP Irp);

好了,第一个WDM版的“Hello World”就介绍到这里,虽然实际上它什么都没有做,但是由于它包含了完整的框架,所以对于初学者来说还是很有参考价值的。至于怎么编译及安装,留待下次再讲,敬请留意。(不是我想卖关子啊,这些步骤实在是很麻烦的,要另外写一篇文章才说得清楚哦!)

WDM驱动程序入门(2)——驱动程序的小秘密

好啦,辛辛苦苦终于写完了程序,让我们编译运行吧!按下Ctrl+F5(嘿嘿,让我们先假设你习惯用VC来写程序),我等啊等……疑?怎么毫无动静的?再看看Output窗口,哇!有几百个错误啊!!不禁头大——这是怎么回事呢?

原来,WDM程序编译出来的并不是我们常见的.exe,而是.sys文件,在未经设置编译环境之前,是不能直接用VC来编译的(这就是为什么会有几百个错误了)。这种类型的文件你可以在WINNT\System32\Drivers里面找到很多。其实驱动程序也是一种PE文件,它同样由DOS MZ header开头,也有完整的DOS stub和PE header,同样拥有Import table和Export table——hoho……那跟普通的PE文件有什么不一样呢?伟大的领袖毛主席教育我们,实践是检验真理的唯一标准。那么就让我们先来做个小剖析,加深对.sys文件的认识吧!(如果你对.sys的内部细节没有兴趣的话,可以略过不看。^_^)

首先祭出Delphi里附带的tdump.exe程序(别问我为什么用这个,这只是纯粹的习惯问题)。让我们键入:

C:\WINNT\System32\Drivers>tdump ccport.sys -em -ee

参数-em是列出Import table,-ee是列出Export table。回车之后,屏幕列出一大堆东西:

C:\WINNT\SYSTEM32\DRIVERS>tdump ccport.sys -em -ee

Turbo Dump Version 5.0.16.12 Copyright ? 1988, 2000 Inprise Corporation

Display of File CCPORT.SYS

IMPORT: NTOSKRNL.EXE={hint:011Fh}.’memcpy’

IMPORT: NTOSKRNL.EXE={hint:003Dh}.’IoDeleteDevice’

IMPORT: NTOSKRNL.EXE={hint:0030h}.’IoAttachDeviceToDeviceStack’

IMPORT: NTOSKRNL.EXE={hint:008Eh}.’KeSetEvent’

IMPORT: NTOSKRNL.EXE={hint:0068h}.’IofCallDriver’

IMPORT: NTOSKRNL.EXE={hint:0095h}.’KeWaitForSingleObject’

IMPORT: NTOSKRNL.EXE={hint:0074h}.’KeInitializeEvent’

IMPORT: NTOSKRNL.EXE={hint:003Fh}.’IoDetachDevice’

IMPORT: NTOSKRNL.EXE={hint:00D3h}.’RtlFreeUnicodeString’

IMPORT: NTOSKRNL.EXE={hint:0077h}.’KeInitializeSpinLock’

IMPORT: NTOSKRNL.EXE={hint:0129h}.’strcpy’

IMPORT: NTOSKRNL.EXE={hint:0121h}.’memset’

IMPORT: NTOSKRNL.EXE={hint:003Ch}.’IoCreateUnprotectedSymbolicLink’

IMPORT: NTOSKRNL.EXE={hint:0038h}.’IoCreateDevice’

IMPORT: NTOSKRNL.EXE={hint:00C2h}.’RtlAnsiStringToUnicodeString’

IMPORT: NTOSKRNL.EXE={hint:0069h}.’IofCompleteRequest’

IMPORT: NTOSKRNL.EXE={hint:0124h}.’sprintf’

IMPORT: NTOSKRNL.EXE={hint:003Eh}.’IoDeleteSymbolicLink’

IMPORT: NTOSKRNL.EXE={hint:0042h}.’IoFreeIrp’

IMPORT: NTOSKRNL.EXE={hint:004Dh}.’IoInitializeIrp’

IMPORT: NTOSKRNL.EXE={hint:002Dh}.’IoAllocateIrp’

IMPORT: NTOSKRNL.EXE={hint:0027h}.’InterlockedExchange’

IMPORT: NTOSKRNL.EXE={hint:0025h}.’InterlockedCompareExchange’

IMPORT: NTOSKRNL.EXE={hint:0035h}.’IoCancelIrp’

IMPORT: NTOSKRNL.EXE={hint:012Ah}.’strlen’

IMPORT: NTOSKRNL.EXE={hint:0126h}.’strcat’

IMPORT: NTOSKRNL.EXE={hint:0114h}.’atoi’

IMPORT: NTOSKRNL.EXE={hint:0128h}.’strcmp’

IMPORT: NTOSKRNL.EXE={hint:0034h}.’IoBuildSynchronousFsdRequest’

IMPORT: NTOSKRNL.EXE={hint:00D5h}.’RtlInitAnsiString’

IMPORT: HAL.DLL={hint:0006h}.’KfAcquireSpinLock’

IMPORT: HAL.DLL={hint:0009h}.’KfReleaseSpinLock’

EXPORT ord:0001=’Vcomm_DriverControl’

我们可以很清楚地看到,它主要调用了NTOSKRNL.EXE和HAL.DLL文件(实际上你会发现,几乎所有的WDM驱动程序都会调用NTOSKRNL.EXE文件,从它的名字你可以看出为什么了吧?),并且输出了一个函数“Vcomm_DriverControl”。这表明,其实.sys跟.exe文件一样,都是一种PE文件来的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。

知道了这些有什么用呢?实际上,由于.sys通常不调用KERNEL32.DLL和USER32.DLL,所以你是不能在设备驱动程序里面调用任何C、C++和Win32函数的,而且也不能用C++关键字new和delete等(可以用malloc和free来代替),而必须使用大量的内核函数。为了读者的方便,下面我列出一些常见的驱动程序可用的内核函数:

Ex… 执行支持

Hal… 硬件抽象层(仅NT/Windows 2000)

Io… I/O管理器(包括即插即用函数)

Ke… 内核

Ks… 内核流IRP管理函数

Mm… 内存管理器

Ob… 对象管理器

Po… 电源管理

Ps… 进程结构

Rtl… 运行时库

Se… 安全引用监视

Zw… 其他函数

最后让我们再来看看,写设备驱动程序时必须注意的一些问题:

1、内核宏

如果查看DDK头文件,会发现有几个内核函数是以宏的方式实现的。这种宏中有几个宏的定义是相当糟糕的。例如,我们看到RemoveHeadList的定义如下:

#define RemoveHeadList(ListHead) \

(ListHead)->Flink; \

{RemoveEntryList((ListHead)->Flink)}

如果以以下方式调用RemoveHeadList,则将编译错误的代码:

if(SomethingInList)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: