虫趣:BAD POOL CALLER (par1: 0x20)
【作者:张佩】 【原文:http://www.yiiyee.cn/Blog/0x19-1/】
内核在管理内存的时候,为了提高内存使用效率,对于小片内存的申请(小于一个PAGE大小),都是通过内存池来操作的。系统里面有两种不同的内存池:分页内存池和非分页内存池。这二者的区别是很明显的:分页内存池所使用的内存页面,随时有可能被分页出去;而非分页内存池所使用的虚拟页面,总是留驻在物理内存中。
对于运行在高中断级别(>=DISPATCH_LEVEL 2)上的代码,它使用的内存只应该是从非分页内存池中申请的。因为系统无法在这些中断级上处理页错误。
除了上面的区别外,系统对两个内存池的管理是极类似的。那么,系统是怎么管理这些内存池的呢?当请求者申请内存的时候,池管理器首先会检查自己当前的储备内存,以确认能否满足申请者的要求,如果可以,则从储备内存中进行分配;否则就重新申请一个新的内存页,并从新内存页中划出一块给申请者,剩下的留作储备内存继续使用。
池管理器把一个页面分成若干个小块(可称为:Entry),第一个Entry是从页的起始地址开始的,而最后一个Entry则恰好到达页尾。每个Entry都有一个描述符,管理器通过这些描述符有效管理这些无数的小块内存。描述符被定义为POOL_HEADER,下面是这个结构体在32位Win7系统上的定义:
0: kd> dt nt!_pool_header
+0x000 PreviousSize : Pos 0, 9 Bits
+0x000 PoolIndex : Pos 9, 7 Bits
+0x002 BlockSize : Pos 0, 9 Bits
+0x002 PoolType : Pos 9, 7 Bits
//+0x000 Ulong1 : Uint4B
+0x004 PoolTag : Uint4B
//+0x004 AllocatorBackTraceIndex : Uint2B
//+0x006 PoolTagHash : Uint2B
在64位系统上,这个结构体定义略有区别,但不影响本文描述。这个描述符位于每个Entry的头部,所以它的名字POOL_HEADER是名副其实的。里面包含5个成员变量,介绍如下:
- PreviouseSize:前一个Entry的长度,粒度为8字节,值1表示8字节。
- BlockSize:当前Entry的长度,粒度为8字节,故值1表示8字节长。
- PoolIndex:不详。
- PoolType:包含当前Entry的一些属性,比如当前块是Free还是Allocated。如果试图释放一个Free状态的内存块,就会出错。
- PoolTag:客户申请内存时设置的Tag值,这个Tag值可便于调试和分析。
PriviouseSize和BlockSize这两个变量很有趣,把它们两个结合在一起有很强大的作用,系统用它们来检查内存池页面的完整性。如果完整性被破坏,系统就会报第一个参数为0x20的BAD_POOL_CALLER(0x19)蓝屏。
这次拿到的dump文件就是这样的一个例子,运行自动分析命令:
0: kd> !analyze -v
*******************************************************************
* *
* Bugcheck Analysis *
* *
******************************************************************* BAD_POOL_HEADER (19)
The pool is already corrupt at the time of the current request.
This may or may not be due to the caller.
The internal pool links must be walked to figure out a possible cause of
the problem, and then special pool applied to the suspect tags or the driver
verifier to a suspect driver.
Arguments:
Arg1: 00000020, a pool block header size is corrupt.
Arg2: 8739ed50, The pool entry we were looking for within the page.
Arg3: 8739ed88, The next pool entry.
Arg4: 18070009, (reserved) Debugging Details:
-------------------------------------------------------
BUGCHECK_STR: 0x19_20 POOL_ADDRESS: 8739ed50 Nonpaged pool DEFAULT_BUCKET_ID: WIN7_DRIVER_FAULT PROCESS_NAME: xxxusermode.exe CURRENT_IRQL: 2 IRP_ADDRESS: 013e8e48 LAST_CONTROL_TRANSFER: from 82c8c588 to 82d2cc6b STACK_TEXT:
99f8f8d8 82c8c588 8739ed58 00000000 b99f0218 nt!ExFreePoolWithTag+0x1b1
99f8f924 82c8403f 013e8e88 99f8f96c 99f8f964 nt!IopCompleteRequest+0xe6
99f8f974 82f3db64 00000000 b13e8e48 b13e8e48 nt!IopfCompleteRequest+0x3b4
99f8f9dc 95af0c5a 8a1c8978 86c97008 99f8f9fc nt!IovCompleteRequest+0x133
99f8f9ec 95aef48f 86c94180 b13e8e48 99f8fa14 ks!CKsFilter::DispatchDeviceIoControl+0x68
99f8f9fc 95adf0ba 86c94180 b13e8e48 b13e8e48 ks!KsDispatchIrp+0xb0
99f8fa14 82f3d6c3 86c94180 b13e8e48 8a98fa48 ks!CKsDevice::PassThroughIrp+0x46
99f8fa38 82c42bd5 00000000 b13e8e48 86c94180 nt!IovCallDriver+0x258
99f8fa4c 82e36bf9 8a98fa48 b13e8e48 b13e8fd8 nt!IofCallDriver+0x1b
99f8fa6c 82e39de2 86c94180 8a98fa48 00000000 nt!IopSynchronousServiceTail+0x1f8
99f8fb08 82e80764 86c94180 b13e8e48 00000000 nt!IopXxxControlFile+0x6aa
99f8fb3c 8932cc90 000003c0 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
WARNING: Stack unwind information not available. Following frames may be wrong.
99f8fd04 82c498a6 000003c0 00000000 00000000 DgSafe+0x19c90
99f8fd04 77967094 000003c0 00000000 00000000 nt!KiSystemServicePostCall
0027ed44 00000000 00000000 00000000 00000000 0x77967094 STACK_COMMAND: kb FOLLOWUP_IP:
ks!CKsFilter::DispatchDeviceIoControl+68
95af0c5a 8bc7 mov eax,edi SYMBOL_STACK_INDEX: 4 SYMBOL_NAME: ks!CKsFilter::DispatchDeviceIoControl+68 FOLLOWUP_NAME: MachineOwner MODULE_NAME: ks IMAGE_NAME: ks.sys DEBUG_FLR_IMAGE_TIMESTAMP: 4ce799d9 FAILURE_BUCKET_ID: 0x19_20_VRF_ks!CKsFilter::DispatchDeviceIoControl+68 BUCKET_ID: 0x19_20_VRF_ks!CKsFilter::DispatchDeviceIoControl+68 Followup: MachineOwner
---------
注意几个地方:
- 蓝屏参数,里面明确指出了错误原因是系统发现有一个pool entry的长度不对,并指出了当前操作的entry(0x8739ed50)和它的下一个entry(0x8739ed88)。为什么同时给出下一个entry,还要下面继续分析。
- 当前进程名,看是否对应了客户程序。
- 调用栈的第0帧,函数是ExFreePoolWithTag。可以知道是在释放内存时出错的。
Windows的内核DDI在32位系统上是使用STDCALL调用协议的,就是通过栈传递参数。这样我们就能得到它的参数:
99f8f8d8 82c8c588 8739ed58 00000000 b99f0218 nt!ExFreePoolWithTag+0x1b1
还原一下,它的调用是这样的:
ExFreePoolWithTag (8739ed58, 0);
对照第一点的两个地址,它和当前操作Entry的地址是很近似的。相差了8个字节,正好是POOL_HEADER结构体的长度:
0: kd> dt nt!_pool_header 8739ed50
+0x000 PreviousSize : 0y000001001 (0x9) // 9 * 8 = 0x48
+0x000 PoolIndex : 0y0000000 (0)
+0x002 BlockSize : 0y000000111 (0x7) // 7 * 8 = 0x38
+0x002 PoolType : 0y0001100 (0xc)
+0x000 Ulong1 : 0x18070009
+0x004 PoolTag : 0x7070534b
+0x004 AllocatorBackTraceIndex : 0x534b
+0x006 PoolTagHash : 0x7070
问题出在哪里呢? 运行一下!pool命令:
0: kd> !pool 8739ed50
Pool page 8739ed50 region is Nonpaged pool
8739e000 size: d0 previous size: 0 (Free) Ntfx
8739e0d0 size: 68 previous size: d0 (Allocated) FMsl
8739e138 size: 68 previous size: 68 (Allocated) EtwR (Protected)
8739e1a0 size: 68 previous size: 68 (Allocated) EtwR (Protected)
8739e208 size: 68 previous size: 68 (Allocated) Mdl
8739e270 size: 18 previous size: 68 (Allocated) MmSi
8739e288 size: 20 previous size: 18 (Allocated) USBB
8739e2a8 size: 68 previous size: 20 (Allocated) FMsl
8739e310 size: 10 previous size: 68 (Free) NSpg
8739e320 size: 48 previous size: 10 (Allocated) Vad
8739e368 size: 10 previous size: 48 (Free) NKBS
8739e378 size: 48 previous size: 10 (Allocated) Vad
8739e3c0 size: c8 previous size: 48 (Allocated) File (Protected)
8739e488 size: 1f8 previous size: c8 (Allocated) z...
8739e680 size: 68 previous size: 1f8 (Allocated) EtwR (Protected)
8739e6e8 size: 18 previous size: 68 (Allocated) MmSi
8739e700 size: 40 previous size: 18 (Allocated) Even (Protected)
8739e740 size: 2e8 previous size: 40 (Allocated) Thre (Protected)
8739ea28 size: 68 previous size: 2e8 (Allocated) FMsl
8739ea90 size: 68 previous size: 68 (Allocated) FMsl
8739eaf8 size: 18 previous size: 68 (Allocated) MmSi
8739eb10 size: 40 previous size: 18 (Allocated) SeTl
8739eb50 size: 40 previous size: 40 (Allocated) Even (Protected)
8739eb90 size: 40 previous size: 40 (Allocated) Even (Protected)
8739ebd0 size: 38 previous size: 40 (Allocated) AlIn
8739ec08 size: 50 previous size: 38 (Free) z...
8739ec58 size: 40 previous size: 50 (Allocated) MmIo
8739ec98 size: 28 previous size: 40 (Allocated) VadS
8739ecc0 size: 48 previous size: 28 (Allocated) Vad
8739ed08 size: 48 previous size: 48 (Allocated) Vad
*8739ed50 size: 38 previous size: 48 (Free ) *KSpp
Pooltag KSpp : irp system buffer property/method/event parameter 8739ed88 doesn't look like a valid small pool allocation, checking to see
if the entire page is actually part of a large page allocation... 8739ed88 is not a valid large pool allocation, checking large session pool...
8739ed88 is freed (or corrupt) pool
Bad allocation size @8739ed88, zero is invalid ***
*** An error (or corruption) in the pool was detected;
*** Attempting to diagnose the problem.
***
*** Use !poolval 8739e000 for more details. Pool page [ 8739e000 ] is __inVALID. Analyzing linked list...
[ 8739ed50 --> 8739edf0 (size = 0xa0 bytes)]: Corrupt region Scanning for single bit errors... None found
!pool在处理任何一个地址的时候,都会跳到地址所在页的起始处开始分析。当前系统的页大小为4K,可算出0x8739ed50对应的页起始地址是0x8739e000,也就是第一条Entry的地址。纵观从第一个entry开始往下,可以看到size和previous size这两个相映成趣的值,恰好是一个“之”型网络:

之型结构从第一个Entry开始,它的previous size为0;前一个entry的size应该和第二个Entry的previous size相等,否则就不对;一直到最后一个entry,都必须把这个关系保持下去。
看看现在这个链表,大概很快就知道问题出在哪里了。当前处理的Entry和它后面一个Entry之间的关系断裂了。
0: kd> dt nt!_pool_header 8739ed88
+0x000 PreviousSize : 0y000000000 (0)
+0x000 PoolIndex : 0y0000000 (0)
+0x002 BlockSize : 0y000000000 (0)
+0x002 PoolType : 0y0000000 (0)
+0x000 Ulong1 : 0
+0x004 PoolTag : 0
+0x004 AllocatorBackTraceIndex : 0
+0x006 PoolTagHash : 0 0: kd> db 8739ed88 L8
8739ed88 00 00 00 00 00 00 00 00
本该是POOL_HEADER的地方,内存已被清空为0。可能什么原因导致这个问题呢?最大的可能性,是驱动申请了一块地址为0x8739ed58d,大小为0x30的内存后,往内存中写入了超过0x30字节的内容,把后面本该属于下一个POOL_HEADER结构体的8个字节内容覆盖了。
对照这个分析进行调试,仔细观察出问题时的用户程序,发现用户程序发送了一个结构体给内核处理,但用户程序和内核定义对同一个结构体的定义不一致,内核所定义的 结构体比用户程序多出两个变量。最终问题得到了解决。
虫趣:BAD POOL CALLER (par1: 0x20)的更多相关文章
- Windows kernel pool 初探(2014.12)
Windows kernel pool 1. 简介 Kernel pool类似于Windows用户层所使用Heap,其为内核组件提供系统资源.在系统初始化的时候,内存管理模块就创建了pool. 严格的 ...
- Improve Scalability With New Thread Pool APIs
Pooled Threads Improve Scalability With New Thread Pool APIs Robert Saccone Portions of this article ...
- CLR thread pool
Thread Pooling https://msdn.microsoft.com/en-us/library/windows/desktop/ms686756(v=vs.85).aspx Threa ...
- 共享内存 share pool (1):heap /extent /chunk/
相关概念 CHUNK: Shared pool物理层面上由许多内存块组成,这些内在块称为chunk.但是chunk是大小不一的,在内存中一个chunk是连续的. EXTENT:由多个连续的chunk组 ...
- Pool:小对象缓存or复用
对象复用 使用链表作为pool来保存要复用的对象. pool字段 obtain recycle 案例1 android.os.Message private static Message sPool; ...
- Erlang pool management -- Emysql pool
从这篇开始,这一系列主要分析在开源社区中,Erlang 相关pool 的管理和使用. 在开源社区,Emysql 是Erlang 较为受欢迎的一个MySQL 驱动. Emysql 对pool 的管理和使 ...
- 启动SpringBoot web项目出现 Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3,....
详细错误信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> ...
- 02-Nov-2017 07:11:56.475 信息 [http-nio-8080-exec-10] com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource. Initializing c3p0 pool...
报错: 02-Nov-2017 07:11:56.475 信息 [http-nio-8080-exec-10] com.mchange.v2.c3p0.impl.AbstractPoolBackedD ...
- js callee,caller学习
原文地址:js callee,caller学习 /* * caller 返回一个对函数的引用,该函数调用了当前函数. * 如果函数是由顶层调用的,那么 caller包含的就是 null . * 如果在 ...
随机推荐
- Zookeeper笔记之基于zk的分布式配置中心
一.场景 & 需求 集群上有很多个节点运行同一个任务,这个任务会有一些可能经常改变的配置参数,要求是当配置参数改变之后能够很快地同步到每个节点上,如果将这些配置参数放在本地文件中则每次都要修改 ...
- Linux Core Dump【转】
转自:http://www.cnblogs.com/hazir/p/linxu_core_dump.html 当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中 ...
- 【linux kernel】 中断处理-中断上半部【转】
转自:http://www.cnblogs.com/embedded-tzp/p/4451354.html 欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地 ...
- Heist
CF#509 div2 A 第一次用自己的号打CF祭. 题目描述 昨晚有一家电子商店被抢劫了. 昨天在商店里的所有键盘都是从x开始按升序编号的.例如,如果x=4,并且商店中有3个键盘,那么编号就为4, ...
- 2017-2018-2 20155225《网络对抗技术》实验一 PC平台逆向破解
2017-2018-2 20155225<网络对抗技术>实验一 PC平台逆向破解 1.直接修改程序机器指令,改变程序执行流程 理清思路: 我们的目标文件是一个linux可执行文件,格式为E ...
- 195 Tenth Line
Given a text file file.txt, print just the 10th line of the file. Example: Assume that file.txt has ...
- shell编程快速入门及实战
shell编程:对于hadoop程序员,通常需要熟悉shell编程,因为shell可以非常方便的运行程序代码. 1.shell文件格式:xxx.sh #!/bin/sh ---shell文件第一行必须 ...
- Spark优化之gc
对于官方Programming Guides的GC优化一节做了阅读. 在这里记录一下我的理解,可能记录的比较混乱没有条理: 我理解其实GC优化的主要目的就是在你的任务执行中使用更少的内存,进行更少的g ...
- 【转】tair与redis比较总结
1. Tair总述 1.1 系统架构 一个Tair集群主要包括3个必选模块:configserver.dataserver和client,一个可选模块:invalidserver.通常情况下,一个集群 ...
- 基于jquery的垂直滚动触发器,多参数可设置。
最近闲来无事,多封装些功能性组件.后期会有更多放出来,大家可以多关注一下. 先上参数: type:"show", 默认为“show”,“show”意为当能够在可 ...