天天看點

driver verifier查找隐藏的記憶體洩露BUG

    verifier是微軟提供的驅動測試工具,可以用來識别記憶體損壞、錯誤處理的 I/O 請求包 (IRP)、無效的直接記憶體通路 (DMA) 緩沖區占用、可能的死鎖以及低資源模拟等情況。在開始菜單->運作中輸入 verifier後,可以彈出如下菜單:

driver verifier查找隐藏的記憶體洩露BUG

選擇預設選項Create standard settings并點選下一步後,出現如下菜單:

driver verifier查找隐藏的記憶體洩露BUG

選擇Automatically select unsigned 并點選下一步後,出現可進行檢查的驅動:

driver verifier查找隐藏的記憶體洩露BUG

點選完成按鈕,并重新開機後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函數。

是以用上文所述的方法可以查找隐藏的記憶體洩露。

繼續閱讀