VS2012編譯除錯WDM驅動(KdPrint無除錯資訊 debugview win7無除錯資訊)

whatday發表於2013-07-19

對於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.


相關文章