verifier是微軟提供的驅動測試工具,可以用來識别記憶體損壞、錯誤處理的 I/O 請求包 (IRP)、無效的直接記憶體通路 (DMA) 緩沖區占用、可能的死鎖以及低資源模拟等情況。在開始菜單->運作中輸入 verifier後,可以彈出如下菜單:
選擇預設選項Create standard settings并點選下一步後,出現如下菜單:
選擇Automatically select unsigned 并點選下一步後,出現可進行檢查的驅動:
點選完成按鈕,并重新開機後verifier便對驅動進行檢查。也可以配置verifier對驅動進行更詳細地檢查(選擇Create custom settings->Select individual settings from a full list)
本文要介紹的是使用verifier對驅動記憶體洩露的檢查。做法比較簡單,加載驅動并挂verifier後,解除安裝驅動,若沒藍屏說明驅動沒有記憶體洩露,藍屏了說明有記憶體洩露。
測試代碼在DriverEntry中申請一塊記憶體并且不做釋放,代碼如下:
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryString)
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING ustrLinkName;
UNICODE_STRING ustrDevName;
PDEVICE_OBJECT DeviceObject;
PVOID pTest;
pTest = ExAllocatePoolWithTag(NonPagedPool, 2000, 'a');
DbgPrint("[ObCallback] DriverEntry: %wZ\n",RegistryString);
// Create dispatch points for device control, create, close.
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
DriverObject->DriverUnload = DriverUnload;
//
RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject,
0,
&ustrDevName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&DeviceObject
);
DbgPrint("[ObCallback] Device Name: %wZ\n",&ustrDevName);
if(!NT_SUCCESS(Status)){
DbgPrint("[ObCallback] IoCreateDevice = 0x%x\n", Status);
return Status;
}
RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
Status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
if(!NT_SUCCESS(Status)){
DbgPrint("[ObCallback] IoCreateSymbolicLink = 0x%x\n", Status);
IoDeleteDevice(DeviceObject);
return Status;
}
DeviceObject->Flags |= DO_BUFFERED_IO;
DbgPrint("[ObCallback] SymbolicLink: %wZ\n",&ustrLinkName);
return STATUS_SUCCESS;
}
編譯生成檔案後,并在verifier中選擇此驅動并重新開機,然後用驅動加載工具加載此驅動,并點選安裝、啟動按鈕,随後點選解除安裝按鈕後,果然藍屏了(附注,原作者可能藍屏了,不過我在Win7上測試,并沒有發生這個現象)。
Windbg.exe加載此藍屏dump,資訊如下:
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_VERIFIER_DETECTED_VIOLATION (c4)
A device driver attempting to corrupt the system has been caught. This is
because the driver was specified in the registry as being suspect (by the
administrator) and the kernel has enabled substantial checking of this driver.
If the driver attempts to corrupt the system, bugchecks 0xC4, 0xC1 and 0xA will
be among the most commonly seen crashes.
Arguments:
Arg1: 00000060, A driver has forgotten to free its pool allocations prior to unloading.
Arg2: 00000000, paged bytes
Arg3: 000007d0, nonpaged bytes,
Arg4: 00000001, total # of (paged+nonpaged) allocations that weren't freed.
To get the name of the driver at fault, type
dp ViBadDriver l1; dS @$p
Then type !verifier 3 drivername.sys for info on the allocations
that were leaked that caused the bugcheck.
Debugging Details:
------------------
Unable to map view of image file
BUGCHECK_STR: 0xc4_60
IMAGE_NAME: ObCallback.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 53578229
MODULE_NAME: ObCallback
FAULTING_MODULE: f885f000 ObCallback
DEFAULT_BUCKET_ID: DRIVER_FAULT
PROCESS_NAME: services.exe
LAST_CONTROL_TRANSFER: from 80650d0c to 804f9cdb
STACK_TEXT:
ef52cacc 80650d0c 000000c4 00000060 00000000 nt!KeBugCheckEx+0x1b
ef52caf4 805a45b5 819ce270 81d2b740 81d2b768 nt!MiVerifyingDriverUnloading+0x12a
ef52cb20 80579c88 819ce270 81d2b750 ef52cb4c nt!MmUnloadSystemImage+0x183
ef52cb30 805b1cf4 81d2b768 81d2b750 00000000 nt!IopDeleteDriver+0x32
ef52cb4c 80523cb5 81d2b768 00000000 ef52cc30 nt!ObpRemoveObjectRoutine+0xe0
ef52cb70 804f57c2 ef52cc30 ef52ccac efc94902 nt!ObfDereferenceObject+0x5f
ef52cc14 8057a937 ef52cd0c 00000000 ef52cc30 nt!IopUnloadDriver+0x28a
ef52cc24 8053e854 ef52cd0c ef52cd48 80500255 nt!NtUnloadDriver+0xf
ef52cc24 80500255 ef52cd0c ef52cd48 80500255 nt!KiSystemServicePostCall
ef52cca0 804f5629 ef52cd0c ef52cd64 00d3f868 nt!ZwUnloadDriver+0x11
ef52cd48 8057a937 00d3f870 00000000 ef52cd64 nt!IopUnloadDriver+0xf1
ef52cd58 8053e854 00d3f870 00d3f878 7c92e514 nt!NtUnloadDriver+0xf
ef52cd58 7c92e514 00d3f870 00d3f878 7c92e514 nt!KiSystemServicePostCall
WARNING: Frame IP not in any known module. Following frames may be wrong.
00d3f878 00000000 00000000 00000000 00000000 0x7c92e514
STACK_COMMAND: kb
FOLLOWUP_NAME: MachineOwner
FAILURE_BUCKET_ID: 0xc4_60_VRF_IMAGE_ObCallback.sys_RECENT
BUCKET_ID: 0xc4_60_VRF_IMAGE_ObCallback.sys_RECENT
Followup: MachineOwner
鍵入!verifier 3 ObCallback.sys指令:
kd> !verifier 3 ObCallback.sys
Verify Level ff ... enabled options are:
Special pool
Special irql
Inject random low-resource API failures
All pool allocations checked on unload
Io subsystem checking enabled
Deadlock detection enabled
Enhanced Io checking enabled
DMA checking enabled
Summary of All Verifier Statistics
RaiseIrqls 0x0
AcquireSpinLocks 0x0
Synch Executions 0x0
Trims 0x0
Pool Allocations Attempted 0x1
Pool Allocations Succeeded 0x1
Pool Allocations Succeeded SpecialPool 0x1
Pool Allocations With NO TAG 0x0
Pool Allocations Failed 0x0
Resource Allocations Failed Deliberately 0x0
Current paged pool allocations 0x0 for 00000000 bytes
Peak paged pool allocations 0x0 for 00000000 bytes
Current nonpaged pool allocations 0x1 for 000007D0 bytes
Peak nonpaged pool allocations 0x1 for 000007D0 bytes
Driver Verification List
Entry State NonPagedPool PagedPool Module
81f95f00 Loaded 000007d0 00000000 ObCallback.sys
Current Pool Allocations 00000001 00000000
Current Pool Bytes 000007d0 00000000
Peak Pool Allocations 00000001 00000000
Peak Pool Bytes 000007d0 00000000
PoolAddress SizeInBytes Tag CallersAddress
829e0830 0x000007d0 a... f8860129
f8860129便是記憶體洩露位址的下一句彙編位址,反彙編上一句(call調用)彙編位址:
kd> u f8860129-6
ObCallback+0x1123:
f8860123 ff151c1086f8 call dword ptr [ObCallback+0x201c (f886101c)]
f8860129 ff750c push dword ptr [ebp+0Ch]
f886012c 68420386f8 push offset ObCallback+0x1342 (f8860342)
f8860131 e8d2000000 call ObCallback+0x1208 (f8860208)
f8860136 8b7508 mov esi,dword ptr [ebp+8]
f8860139 8b3d0c1086f8 mov edi,dword ptr [ObCallback+0x200c (f886100c)]
f886013f 59 pop ecx
f8860140 59 pop ecx
kd>
ObCallback+0x1123:
f8860123 ff151c1086f8 call dword ptr [ObCallback+0x201c (f886101c)]
f8860129 ff750c push dword ptr [ebp+0Ch]
f886012c 68420386f8 push offset ObCallback+0x1342 (f8860342)
f8860131 e8d2000000 call ObCallback+0x1208 (f8860208)
f8860136 8b7508 mov esi,dword ptr [ebp+8]
f8860139 8b3d0c1086f8 mov edi,dword ptr [ObCallback+0x200c (f886100c)]
f886013f 59 pop ecx
f8860140 59 pop ecx
f8860123位址處便是記憶體洩露的地方。檢視call dword ptr [ObCallback+0x201c (f886101c)]中的f886101c的内容,并反彙編:
kd> dd f886101c l1
f886101c 80651a68
kd> u 80651a68
nt!VerifierAllocatePoolWithTag:
80651a68 8bff mov edi,edi
80651a6a 55 push ebp
80651a6b 8bec mov ebp,esp
80651a6d 51 push ecx
80651a6e 51 push ecx
80651a6f 8d45f8 lea eax,[ebp-8]
80651a72 50 push eax
80651a73 8d45fc lea eax,[ebp-4]
對應的函數是VerifierAllocatePoolWithTag,源代碼其實是ExAllocatePoolWithTag。因為driver verifier Hook了驅動的輸入表,把ExAllocatePoolWithTag函數替換為verifier 的VerifierAllocatePoolWithTag函數。
是以用上文所述的方法可以查找隐藏的記憶體洩露。