天天看點

windbg中通過檔案句柄查找裝置(!handle/!fileobj/!devobj指令)

    有時,在驅動程式中會調用ZwCreateFile獲得裝置句柄,然後儲存在裝置擴充區域中供其他例程使用。由于驅動程式經常被動調用----執行的上下文可能不是同一個線程----會獲得錯誤的句柄。那麼是否存在某些調試指令供我們在開發階段判斷所用句柄正是某個裝置的句柄?強大的windbg提供了!handle/!fileobj/!devobj這些擴充指令來實作這個目的(注意這些指令要正确設定調試符号的路徑)。

    為了示範這些功能,我寫了2個驅動:a.SampleChar,一個挂載在root總線之上的字元驅動,建立了一個名為\\Deivce\SampleChar的虛拟裝置;b.FilterSampleChar,同樣是挂載在root總線之上的虛拟字元驅動,建立了一個名為\\Device\FilterSample裝置。應用程式打開裝置\\Device\FilterSample時,FilterSampleChar在Create派遣函數中調用ZwCreateFile打開裝置\\Deivce\SampleChar并獲得句柄。下面是FilterSampleChar代碼片段:

NTSTATUS FilterSampleCharCreateClose(PDEVICE_OBJECT devObj, PIRP irp)
{
  HANDLE sampleCharDev;
  IO_STACK_LOCATION* currStack = IoGetCurrentIrpStackLocation(irp);
  NTSTATUS status = STATUS_SUCCESS;
  UNICODE_STRING devName;
  OBJECT_ATTRIBUTES fileAttr;
  IO_STATUS_BLOCK ioStatus;
  FilterSampleDriverCtx* devCtx = (FilterSampleDriverCtx*)devObj->DeviceExtension;

  RtlInitUnicodeString(&devName, L"\\Device\\SampleChar");
  InitializeObjectAttributes(&fileAttr, 
              &devName,
              OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, 
              NULL,NULL);

  switch (currStack->MajorFunction)
  {
  case IRP_MJ_CREATE:
    status = ZwCreateFile(&sampleCharDev, 
            GENERIC_READ|SYNCHRONIZE, 
            &fileAttr, 
            &ioStatus,NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ,
            FILE_OPEN_IF, 
            FILE_SYNCHRONOUS_IO_NONALERT, 
            NULL, 0);

    if (status != STATUS_SUCCESS)    ----> 1)
    {
      irp->IoStatus.Information = 0;
      irp->IoStatus.Status = status;
      IoCompleteRequest(irp, IO_NO_INCREMENT);
      return status;
    }
    else
    {
      devCtx->targetDevHd = sampleCharDev;
    }
    break;
  case IRP_MJ_CLEANUP:
    ZwClose(devCtx->targetDevHd);
    break;
  }
  irp->IoStatus.Information = 0;
  irp->IoStatus.Status = STATUS_SUCCESS;
  IoCompleteRequest(irp, IO_NO_INCREMENT);

  return status;
}      

在1)處下斷點,然後執行測試程式,windbg馬上就會停留在斷點處:

int main()
{
  char interfaceBuff[128] = { 0 };
  DWORD lstErr;
  char readBuff[4096] = { 0 };
  DWORD len = 0, writeLen, readLen;

  hDev = CreateFileA("\\\\.\\filterSampleChar",
    GENERIC_ALL,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, 0, NULL);      
windbg中通過檔案句柄查找裝置(!handle/!fileobj/!devobj指令)

首先檢視傳回句柄sampleCharDev的值:

kd> ?? sampleCharDev
void * 0x80000918      

句柄值0x80000918。随後,用!handle指令檢視句柄資訊

kd> !handle 80000918 

PROCESS 877bbd40  SessionId: 1  Cid: 027c    Peb: 7ffda000  ParentCid: 08e0
    DirBase: 7ffd9480  ObjectTable: a1da6070  HandleCount:  13.
    Image: TestFilterSampleChar.exe

Kernel handle table at 9d800000 with 485 entries in use

80000918: Object: 86d0f650  GrantedAccess: 00120089 Entry: 9d801230
Object: 86d0f650  Type: (87602938) File
    ObjectHeader: 86d0f638 (new version)
        HandleCount: 1  PointerCount: 1      

這個指令顯示了句柄所屬的程序資訊,對象資訊:object 86d0f650,對象類型:file

知道這是檔案對象後,可以通過!fileobj獲得檔案本身的資訊:

kd> !fileobj 86d0f650  

Device Object: 0x89bf1b18   \Driver\SampleChar
Vpb is NULL
Event signalled

Flags:  0x40002
  Synchronous IO
  Handle Created

CurrentByteOffset: 0      

通過這個指令,可知:這個檔案是打開裝置對象Device Object:0x89bf1b18時産生的檔案句柄,該裝置對象由驅動\Driver\SampleChar建立;另外還有一些檔案指針資訊,如CurrentByteOffset。

有了裝置對象的位址,就可以通過!devstack指令獲得裝置資訊:

kd> !devstack 0x89bf1b18   
  !DevObj   !DrvObj            !DevExt   ObjectName
> 89bf1b18  \Driver\SampleChar 89bf1bd0  SampleChar
  8761b980  \Driver\PnpManager 8761ba38  00000045
!DevNode 8761b7c8 :
  DeviceInst is "Root\SYSTEM\0003"
  ServiceName is "SampleChar"      

!devstack輸出的裝置棧資訊和文章開頭所列的代碼意圖一緻,應該足以判斷ZwCreateFile傳回的裝置句柄背後就是裝置對象\\Device\SampleChar