虫趣: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 . * 如果在 ...
随机推荐
- HTML+CSS写下拉菜单
今天学习了使用HTML+CSS实现下拉菜单效果,在这个例子中,我学到了如下知识点: 设置背景图片(background-image.background-size) 如何让无序列表横向显示(float ...
- malloc 函数详解
很多学过C的人对malloc都不是很了解,知道使用malloc要加头文件,知道malloc是分配一块连续的内存,知道和free函数是一起用的.但是但是: 一部分人还是将:malloc当作系统所提供的或 ...
- 理解 Memory barrier(内存屏障)【转】
转自:http://name5566.com/4535.html 参考文献列表:http://en.wikipedia.org/wiki/Memory_barrierhttp://en.wikiped ...
- retrying模块的学习
retrying模块的学习 我们在写爬虫的过程中,经常遇到爬取失败的情况,这个时候我们一般会通过try块去进行重试,但是每次都写那么一堆try块,真的是太麻烦,所以今天就来说一个比较pythonic的 ...
- cefSharp获取百度搜索结果页面的源码
using CefSharp; using CefSharp.WinForms; using System; using System.Collections.Generic; using Syste ...
- MVC layout 命名空间引用问题
虽然用MVC做了很多项目,但是都是在别人搭好的框架上实现 今天碰到一个很简单的命名空间引用问题 如图所示,Scripts和Styles 都没有引用命名空间 解决方法一: 直接使用 System.Web ...
- hdu 4348 To the moon (主席树)
版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 4348 题意: 一个长度为n的数组,4种操作 : (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 . (2)Q ...
- 三、vue脚手架工具vue-cli的使用
1.vue-cli构建 vue-cli工具构建:https://blog.csdn.net/u013182762/article/details/53021374 npm的镜像替换成淘宝 2.项目运行 ...
- 【Unity_UWP】Unity 工程发布win10 UWP 时的本地文件读取 (上篇)
Universal Windows Platform(UWP)是微软Windows10专用的通用应用平台,其目的在于在统一操作系统下控制所有智能电子设备. 自从Unity 5.2之后,配合VS 201 ...
- UFLDL 教程学习笔记(一)
ufdl的新教程,从基础学起.第一节讲的是线性回归.主要目的是熟悉目标函数,计算梯度和优化. 按着教程写完代码后,总是编译出错,一查是mex的原因,实在不想整了. 这位博主用的是向量,比较简洁:htt ...