问题的提出:
(类似windbg ~*kb命令)的调用栈不就行了。没错,对于应用程序而言,这样做十有八九已经定位了。但是,对于驱动程序而言,它运行的上下文可能并不固定在某一进程,回溯内核中所有线程的方式不大可取,因此只能换一种思路定位去问题。我的选择是搜索KEVENT所在的内存,然后查看KEVENT!_DISPATCHER_HEADER!SignalState的值:
kd> dt nt!_KEVENT
+0x000 Header : _DISPATCHER_HEADER
kd> dt nt!_DISPATCHER_HEADER
+0x000 Type : UChar
...
+0x004 SignalState : Int4B
+0x008 WaitListHead : _LIST_ENTRY
思路:
一般而言,由于KEVENT多用于多线程同步(不可能在栈上分配),驱动程序主要通过2种方式分配并初始化KEVENT:1.在调用IoCreateDevice后,KEVENT作为设备扩展(devObj->DeviceExtension)中的某一个域,被初始化;2.调用ExAllocatePoolWithTag分配堆内存,并初始化。看到这,你可能会怀疑我是不是用windbg的s命令(search搜索内存命令)来定位信号量?这工作量无异于大海捞针!当然不是,你的问题,我需要按上面提到的2种情况分类讨论:对于情况1,通过!devobj命令获得设备拓展来定位KEVENT即可;对于情况2,通过!poolused&!poolfind命令的组合来定位。下面让我们一起来看具体步骤。
情况1,如果KEVENT作为设备拓展的一部分存在:
虽然event设备扩展中不存在KEVENT对象,但仍然可以用以演示说明。首先查找设备对象的地址:通过!drvobj和!devobj命令完成:
kd> !drvobj event
Driver object (fffffa801b391e70) is for:
\Driver\event
Driver Extension List: (id , addr)
Device Object list:
fffffa80191f7e20
kd> !devobj fffffa80191f7e20
Device object (fffffa80191f7e20) is for:
Event_Sample \Driver\event DriverObject fffffa801b391e70
Current Irp 00000000 RefCount 0 Type 00000022 Flags 00000044
SecurityDescriptor fffff8a0000840e0 DevExt fffffa80191f7f70 <-这才是设备拓展,右边的DevObjExt并不是。具体请查看情景分析相关章节 DevObjExt fffffa80191f7f90
ExtensionFlags (0x00000800) DOE_DEFAULT_SD_PRESENT
Characteristics (0x00000100) FILE_DEVICE_SECURE_OPEN
Device queue is not busy.
在这个例子中,event驱动创建的设备对象地址为0xfffffa80191f7e20;其设备扩展为0xfffffa80191f7f70。把这个地址作为参数,传递给dt命令,即可获得设备扩展各个域的当前状态:
kd> dt event!_DEVICE_EXTENSION 0xfffffa80191f7f70
+0x000 Self : 0xfffffa80`191f7e20 _DEVICE_OBJECT ;Self指向0xfffffa80191f7f70,这符合!drvobj的输出
+0x008 EventQueueHead : _LIST_ENTRY [ 0xfffffa80`191f7f78 - 0xfffffa80`191f7f78 ]
+0x018 QueueLock : 0
情况2,KEVENT是通过ExAllocatePoolWithTag系列函数动态分配的:
对于这种情况,定位需要用到ExAllocatePoolWithTag在分配内存时,制定的Tag参数。event定义的Tag值如下:
event.c:
case IRP_MJ_CREATE:
DebugPrint(("IRP_MJ_CREATE\n"));
fileContext = ExAllocatePoolWithQuotaTag(NonPagedPool,
sizeof(FILE_CONTEXT),
TAG);
event.h:
#define TAG (ULONG)'TEVE' //因为CPU以小端序存储数据,所以,代码中定义的'TEVE',在内存中看起来就是EVET。
kd> bl
0 e Disable Clear fffff880`02f3d1b0 0001 (0001) event!EventCreateClose+0xd0
此时,可以查看fileContext的值,并定位其中各个域的状态:
kd> ?? fileContext
struct _FILE_CONTEXT * 0xfffffa80`191d16e0
+0x000 FileRundownLock : _IO_REMOVE_LOCK
kd> dt event!_FILE_CONTEXT 0xfffffa80`191d16e0
+0x000 FileRundownLock : _IO_REMOVE_LOCK
回到本文的主题,程序死锁时,我们可能并不知道是哪出的问题,这时,只能用Tag,打一套组合拳去定位:
首先,查看驱动分配的pool:
kd> !poolused 2 EVET ;2代表查找非分页内存
.
Sorting by NonPaged Pool Consumed
NonPaged Paged
Tag Allocs Used Allocs Used
EVET 1 144 0 0 UNKNOWN pooltag 'EVET', please update pooltag.txt
TOTAL 1 144 0 0ram
kd> !poolfind EVET -nonpaged
Scanning large pool allocation table for tag 0x54455645 (EVET) (fffffa801adbc000 : fffffa801ae7c000)
Searching nonpaged pool (fffffa8018c04000 : fffffa8075400000) for tag 0x54455645 (EVET)
fffffa80191d16e0 : tag EVET, size 0x80, Nonpaged pool, quota process fffffa80191d2b30
!poolused显示标为EVET的pool的总体情况,而!poolfind则显示各个pool的情况。!poolfind的输出0xfffffa80191d16e0和?? fileContext的输出有点出路,这是因为每个pool对象有POOL_HEADER头结构,就如同应用程序分配堆时,为每个请求的内存添加一个HEAP_ENTRY一样。!poolfind输出的是从POOL_HEADER开始的内存,而??fileContext则显示pool正文的起始。所以,我们在真实的应用中还需要加上这段偏移,才能获得真正的内容。
标签:驱动程序,kd,内存,EVET,event,pool,KEVENT From: https://blog.51cto.com/u_13927568/5831394