在嘗試運作《竹林蹊徑 深入淺出Windows驅動開發》的第一個例子-HelloDRIVER時,在XP下沒有問題,但在Win7下卻發生藍屏,藍屏發生點在于解除安裝函數DriverUnload。
先看看解除安裝驅動的代碼
VOID DriverUnload (
__in PDRIVER_OBJECT DriverObject
)
{
PDEVICE_OBJECT deviceObject;
UNICODE_STRING linkName;
KdPrint(("Enter HelloDRIVER DriverUnload!\n"));
deviceObject = DriverObject->DeviceObject;
while(NULL != deviceObject)
{
PDEVICE_EXTENSION deviceExtesion =
(PDEVICE_EXTENSION)deviceObject->DeviceExtension;
// 删除符号連結與裝置
linkName = deviceExtesion->SymbolicLink;
IoDeleteSymbolicLink(&linkName);
deviceObject = deviceObject->NextDevice;
IoDeleteDevice(deviceExtesion->DeviceObject);
}
KdPrint(("End of HelloDRIVER DriverUnload!\n"));
}
很通用的解除安裝驅動例程,按道理是不會出現問題的,從dump檔案進行進一步分析,定位到deviceObject = deviceObject->NextDevice;這一句藍屏,複制操作會導緻藍屏?什麼鬼,再看下調用堆棧
原來還沒到指派語句,在處于IoDeleteSymbolicLink階段就發生藍屏了,于是就斷點調試看下IoDeleteSymbolicLink的參數linkName是否正常,一調試發現memory can't access, 為什麼會出現記憶體不可通路,于是追溯到DriverEntry,下面看下這個函數裡面做了什麼操作
NTSTATUS DriverEntry (
__in PDRIVER_OBJECT DriverObject,
__in PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING symbolicLink;
UNICODE_STRING deviceName;
ULONG i;
KdPrint(("Enter HelloDRIVER DriverEntry!\n"));
UNREFERENCED_PARAMETER(RegistryPath);
RtlInitUnicodeString(&deviceName, L"\\Device\\HelloDRIVER");
// 處理派遣例程
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = DefaultDispatch;
}
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DefaultDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DefaultDispatch;
DriverObject->MajorFunction[IRP_MJ_READ] = DefaultDispatch;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DefaultDispatch;
// 建立裝置
status = IoCreateDevice( DriverObject,
sizeof(DEVICE_EXTENSION),
&deviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject);
if(!NT_SUCCESS(status))
{
return status;
}
deviceObject->Flags = DO_BUFFERED_IO;
deviceExtension = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;
deviceExtension->DeviceObject = deviceObject;
deviceExtension->DeviceName = deviceName;
RtlInitUnicodeString(&symbolicLink, L"\\??\\HelloDRIVER");
deviceExtension->SymbolicLink = symbolicLink;
// 建立符号連結
status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
if(!NT_SUCCESS(status))
{
IoDeleteDevice(deviceObject);
return status;
}
KdPrint(("End of HelloDRIVER DriverEntry!\n"));
return status;
}
重點在于這兩句
RtlInitUnicodeString(&symbolicLink, L"\\??\\HelloDRIVER");
deviceExtension->SymbolicLink = symbolicLink;
在網上查找資料RtlInitUnicodeString并沒有動态配置設定記憶體,而是指向應用,而由于
#pragma alloc_text(INIT, DriverEntry)
DriverEntry函數是運作完後就退出記憶體,是以 RtlInitUnicodeString(&deviceName, L"\\Device\\HelloDRIVER");這個指派方式不可靠。
解決方案:
在頭檔案或者函數外聲明一個全局變量來存儲符号連結,如
WCHAR linkNameBuffer[] = L"\\??\\HelloDRIVER";
然後再在函數内
RtlInitUnicodeString(&symbolicLink, linkNameBuffer);
即可解決讀取符号連結不可通路的問題。
總結:
1.Win7會出現藍屏,而XP不會,可能涉及到IoDeleteSymbolicLink裡面的實作,會判斷字元串的合法性問題
2.INIT和PAGE的差別,在驅動加載入口函數要注意字元串變量的生命周期
3.學會利用windbg進行調試分析