对于WDM驱动 VS2012有向导可以新建WDM项目 如图 这点说明不用自己配置 文件目录 C/C++ 选项 LINK 选项 等一系列的参数 比以前方便了不少
新建以后是空项目 放入《windows驱动开发技术详解》中第一章的WDM代码
分别是: HelloWDM.h
#if __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#ifdef __cplusplus
}
#endif
typedef struct _DEVICE_EXTERSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStatckDevice;
UNICODE_STRING ustrDeviceName; //设备名
UNICODE_STRING ustrSymLinkName; //符号链接名
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_set("INIT")
#define PAGEDDATA data_set("PAGE")
#define LOCKEDDATA data_set()
#define INITDATA data_set("INIT")
#define arraysize(p) (sizeof(p)/sizeof((p)[0]))
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject);
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp);
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp);
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
HelloWDM.cpp
#include "HelloWDM.h"
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Entry DriverEntry\n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry\n"));
return STATUS_SUCCESS;
}
#pragma PAGECODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName, L"\\Device\\MyWDMDevice");
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo);
if(!NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
pdx->NextStatckDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName, L"\\DosDevices\\HelloWDM");
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName);
if(!NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName, &devName);
if(!NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp\n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp)=
{
DefaultPnpHandler,
DefaultPnpHandler,
HandleRemoveDevice,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
};
ULONG fcn = stack->MinorFunction;
if(fcn >= arraysize(fcntab))
{
status = DefaultPnpHandler(pdx, Irp);
return status;
}
status = (*fcntab[fcn])(pdx, Irp);
KdPrint(("Leave HelloWDMPnp\n"));
return status;
}
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler\n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler\n"));
return IoCallDriver(pdx->NextStatckDevice, Irp);
}
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandlerRemoveDevice\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
if(pdx->NextStatckDevice)
IoDetachDevice(pdx->NextStatckDevice);
IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandlerRemoveDevice\n"));
return status;
}
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
KdPrint(("Leave HelloWdmDispatchRoutine\n"));
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload\n"));
KdPrint(("Leave HelloWDMUnload\n"));
}
代码放置后工程目录如图:
项目默认是vista的debug版本 修改为 win7 debug
然后build工程 出现错误:
错误原因是因为 警告级别设置过高 将警告级别改为W3等级 如图
设置后编译通过 生成了MyDriver1.sys文件 MyDriver1.inf文件 这个时候修改inf文件为:如果没有产生INF文件则自己新建一个
;--------- Version Section ---------------------------------------------------
[Version]
Signature="$CHICAGO$";
Provider=Zhangfan_Device
DriverVer=07/19/2013,15.16.19.288
; If device fits one of the standard classes, use the name and GUID here,
; otherwise create your own device class and GUID as this example shows.
Class=ZhangfanDevice
ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B0}
;--------- SourceDiskNames and SourceDiskFiles Section -----------------------
; These sections identify source disks and files for installation. They are
; shown here as an example, but commented out.
[SourceDisksNames]
1 = "HelloWDM",Disk1,,
[SourceDisksFiles]
MyDriver1.sys = 1,,
;--------- ClassInstall/ClassInstall32 Section -------------------------------
; Not necessary if using a standard class
; 9X Style
[ClassInstall]
Addreg=Class_AddReg
; NT Style
[ClassInstall32]
Addreg=Class_AddReg
[Class_AddReg]
HKR,,,,%DeviceClassName%
HKR,,Icon,,"-5"
;--------- DestinationDirs Section -------------------------------------------
[DestinationDirs]
YouMark_Files_Driver = 10,System32\Drivers
;--------- Manufacturer and Models Sections ----------------------------------
[Manufacturer]
%MfgName%=Mfg0
[Mfg0]
; PCI hardware Ids use the form
; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd
;改成你自己的ID
%DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999
;---------- DDInstall Sections -----------------------------------------------
; --------- Windows 9X -----------------
; Experimentation has shown that DDInstall root names greater than 19 characters
; cause problems in Windows 98
[YouMark_DDI]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_9X_AddReg
[YouMark_9X_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,MyDriver1.sys
HKR, "Parameters", "BreakOnEntry", 0x00010001, 0
; --------- Windows NT -----------------
[YouMark_DDI.NT]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_NT_AddReg
[YouMark_DDI.NT.Services]
Addservice = HelloWDM, 0x00000002, YouMark_AddService
[YouMark_AddService]
DisplayName = %SvcDesc%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %10%\System32\Drivers\MyDriver1.sys
[YouMark_NT_AddReg]
HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\
"BreakOnEntry", 0x00010001, 0
; --------- Files (common) -------------
[YouMark_Files_Driver]
MyDriver1.sys
;--------- Strings Section ---------------------------------------------------
[Strings]
ProviderName="Zhangfan."
MfgName="Zhangfan Soft"
DeviceDesc="Hello World WDM!"
DeviceClassName="Zhangfan_Device"
SvcDesc="Zhangfan"
接下来是加载驱动 使用EzDriverInstaller工具 下载地址:http://download.csdn.net/detail/whatday/5782597
把MyDriver1.sys和MyDriver1.inf拷贝到VM的win7下,用EzDriverInstaller打开inf文件 如图
这时打开Dbgview 进行调试信息的监视
点击 Add New Device按钮后 设备添加成功 但是DbgView中没有出现调试信息
原因在于 win7/vista下需要修改调试信息过滤的键值 具体做法是
新建一个1.reg文件,写入如下内容:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter]
"DEFAULT"=dword:00000008
导入重启之后,OK。键值00000008不行 可以改为0000000f尝试 我这边测试08是成功的。
重启后再次开启 EzDriverInstaller 和 DbgView 再次点击 Add New Device 这时发现已经有调试信息了
接下来就是调试WDM驱动,便于测试在驱动中添加一个 读取制定函数SSDT地址的功能 修改后的代码如下:
HelloWDM.h:
#include "HelloWDM.h"
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Entry DriverEntry\n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry\n"));
DbgBreakPoint();
ULONG SSDT_NtOpenProcess_Cur_Addr;
ULONG dwAddr = (ULONG)KeServiceDescriptorTable->pSSDTBase;
KdPrint(("当前SSDT的基地址是:0x%x\n", dwAddr));
SSDT_NtOpenProcess_Cur_Addr = *(PULONG)(dwAddr + 4*190);
KdPrint(("当前NtOpenProcess的地址是0x%x\n", SSDT_NtOpenProcess_Cur_Addr));
return STATUS_SUCCESS;
}
#pragma PAGECODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName, L"\\Device\\MyWDMDevice");
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo);
if(!NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
pdx->NextStatckDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName, L"\\DosDevices\\HelloWDM");
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName, &(UNICODE_STRING)devName);
if(!NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName, &devName);
if(!NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp\n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp)=
{
DefaultPnpHandler,
DefaultPnpHandler,
HandleRemoveDevice,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
DefaultPnpHandler,
};
ULONG fcn = stack->MinorFunction;
if(fcn >= arraysize(fcntab))
{
status = DefaultPnpHandler(pdx, Irp);
return status;
}
status = (*fcntab[fcn])(pdx, Irp);
KdPrint(("Leave HelloWDMPnp\n"));
return status;
}
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler\n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler\n"));
return IoCallDriver(pdx->NextStatckDevice, Irp);
}
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandlerRemoveDevice\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
if(pdx->NextStatckDevice)
IoDetachDevice(pdx->NextStatckDevice);
IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandlerRemoveDevice\n"));
return status;
}
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine\n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
KdPrint(("Leave HelloWdmDispatchRoutine\n"));
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload\n"));
KdPrint(("Leave HelloWDMUnload\n"));
}
由于添加了断点代码所以需要打开windbg双机调试,代码编译后覆盖WIN7中的sys文件 用EzDriverInstaller工具加载INF文件 这里可以点击“Update Driver” 发现和以前一样,
没有打印出SSDT的相关信息 ,初步估计还是加载了上次的SYS文件,WIN7系统肯定有备份存档之类的,在C盘搜索MyDriver1.sys 得到4个路径如图:
其中第一个路径 刚才已经覆盖过 但是驱动显示的结果没有变化所以 这里先pass掉
然后依次测试第二 第三,当测试到第三个路径时出现权限问题:
返回上一级目录 对该目录右键 设置管理员取得所有权:
设置后就可以成功覆盖SYS文件了 用EzDriverInstaller工具加载 发现覆盖驱动后 调试信息发生了变化 如图:
一般来说win7的驱动备份文件夹都是各个优化软件的减肥首选,由此看来win7的驱动备份文件夹也不是完全无用的,至少更新驱动的时候还是需要用到。
至此VS2012编译调试WDM驱动就完毕了,现在可以随意的修改调试我们的WDM程序了。
这里需要注意 “Add New Device”测试完毕后 一定要“Remove Device”因为如果重启WIN7 进入正常模式(非DEBUG模式)它会自动加载我们的WDM驱动 如果驱动中有断点API或错误 就会蓝屏挂掉。当然如果是进入DEBUG模式,然后双机调试就可以看到在windbg中断下来了,断点位置就是我们程序中设置的断点API代码。
最后再附上 win7/ vista下为什么需要修改调试信息过滤键值的原因 原文如下
在OSR查了下资料,摘录如下:
The problem: Your DbgPrint or KdPrint messages don't appear in WinDbg (or KD) when you run your driver on Windows Vista.
The reason? Vista automatically maps DbgPrint and friends to DbgPrintEx. Now, you may recall that DbgPrintEx allows you to control the conditions under which messages will be sent to the kernel debugger by filtering messages via a component name and level in the function call and an associated filter mask in either the registry or in memory.
In Vista, DbgPrint and KdPrint are mapped to component "DPFLTR_DEFAULT_ID" and level "DPFLTR_INFO_LEVEL". Of course, in Vista, xxx_INFO_LEVEL output is disabled by default. So, by default, your DbgPrint/KdPrint doesn't get sent to the kernel debugger.
How to fix it? Two choices:
-
Enable output of DbgPrint/KdPrint messages by default --Open the key "HKLM/SYSTEM/CCS/Control/Session Manager/Debug Print Filter". Under this key, create a value with the name "DEFAULT" Set the value of this key equal to the DWORD value 8 to enable xxx_INFO_LEVEL output as well as xxx_ERROR_LEVEL output. Or try setting the mask to 0xF so you get all output. You must reboot for these changes to take effect.
-
Specifically change the component filter mast for DPFLTR. In early releases of Vista/LH you changed the default printout mask by specifying a mask value for the DWORD at Kd_DPFLTR_MASK ("ed Kd_DPFLTR_MASK"). In build 5308 (the February CTP of Vista), it seems that the mask variable has changed and you need to set the mask value for the DWORD at Kd_DEFAULT_MASK ("ed Kd_DEFAULT_MASK). In either case, specify 8 to enable DPFLTR_INFO_LEVEL output in addition to DPFLTR_ERROR_LEVEL output, or 0xF to get all levels of output.
See the WDK documentation for Reading and Filtering Debugging Messages (follow the path: Driver Development Tools/Tools for Debugging Drivers/Using Debugging Code in a Driver/Debugging Code Overview) for the complete details on the use of DbgPrintEx/KdPrintEx. Or look at the Debugging Tools For Windows documentation (Appendix A) on DbgPrintEx.