ListEntry 遍历宏

本文详细介绍了如何使用自定义搜索列表函数MySearchList来查找特定实例ID的过滤器上下文。通过遍历AdvFcbHeader->FilterContexts列表,并检查每个元素的OwnerId和InstanceId属性,最终返回匹配条件的过滤器上下文。

 #define MySearchList(pHdr, Ptr) \
    for ( Ptr = (pHdr)->Flink;  Ptr != (pHdr);  Ptr = Ptr->Flink )


if ( ARGUMENT_PRESENT(InstanceId) ) {



        MySearchList (&AdvFcbHeader->FilterContexts, list) {


            ctx  = CONTAINING_RECORD(list, FSRTL_PER_STREAM_CONTEXT, Links);
            if (ctx->OwnerId == OwnerId && ctx->InstanceId == InstanceId) {


                rtnCtx = ctx;
                break;
            }
        }

    } 
kd> x nt!MmPfnDatabase fffff805`7b8fc500 nt!MmPfnDatabase = <no type information> kd> dt _mmpfn fffff805`7b8fc500+0x460f7*0x30 nt!_MMPFN +0x000 ListEntry : _LIST_ENTRY [ 0x470348fa`8b4ce3f7 - 0xc4034900`d7834908 ] +0x000 TreeNode : _RTL_BALANCED_NODE +0x000 u1 : <anonymous-tag> +0x008 PteAddress : 0xc4034900`d7834908 _MMPTE +0x008 PteLong : 0xc4034900`d7834908 +0x010 OriginalPte : _MMPTE +0x018 u2 : _MIPFNBLINK +0x020 u3 : <anonymous-tag> +0x024 NodeBlinkLow : 0x1047 +0x026 Unused : 0y1001 +0x026 Unused2 : 0y0100 +0x027 ViewCount : 0x83 '' +0x027 NodeFlinkLow : 0x83 '' +0x027 ModifiedListBucketIndex : 0y0011 +0x027 AnchorLargePageSize : 0y11 +0x028 u4 : <anonymous-tag> kd> dt _mmpfn fffff805`7b8fc500+0x4609*0x30 nt!_MMPFN +0x000 ListEntry : _LIST_ENTRY [ 0x006f006c`006c0041 - 0x00000064`00650077 ] +0x000 TreeNode : _RTL_BALANCED_NODE +0x000 u1 : <anonymous-tag> +0x008 PteAddress : 0x00000064`00650077 _MMPTE +0x008 PteLong : 0x00000064`00650077 +0x010 OriginalPte : _MMPTE +0x018 u2 : _MIPFNBLINK +0x020 u3 : <anonymous-tag> +0x024 NodeBlinkLow : 0x54 +0x026 Unused : 0y0101 +0x026 Unused2 : 0y0110 +0x027 ViewCount : 0 '' +0x027 NodeFlinkLow : 0 '' +0x027 ModifiedListBucketIndex : 0y0000 +0x027 AnchorLargePageSize : 0y00 +0x028 u4 : <anonymous-tag> kd> dx -id 0,0,ffffbd046545f1c0 -r1 (*((ntkrnlmp!_LIST_ENTRY *)0xfffff8057c61f350)) (*((ntkrnlmp!_LIST_ENTRY *)0xfffff8057c61f350)) [Type: _LIST_ENTRY] [+0x000] Flink : 0x470348fa8b4ce3f7 [Type: _LIST_ENTRY *] [+0x008] Blink : 0xc4034900d7834908 [Type: _LIST_ENTRY *]
最新发布
07-21
<think>我们正在处理的问题是关于Windows内核调试中的MMPFN结构和页表项(PTE)分析,以及内存错误问题。根据用户提供的引用内容,我们可以总结如下关键点: 1. **MmPfnDatabase**:这是一个全局数组,包含系统中所有物理页帧的MMPFN结构体。每个物理页对应一个MMPFN结构体,它们连续存放。通过WinDbg命令`dd MmPfnDatabase`可以查看其地址[^1][^3][^4]。 2. **MMPFN结构体**:该结构体描述了物理页帧的状态信息,包括页帧的状态(如活动、备用、已修改等)、链表指针等。物理页的状态通过不同的链表维护(如空闲链表、零化链表等)[^3]。 3. **页表项(PTE)**:用于将线性地址映射到物理地址。当PTE的P标志位为0时,表示该页不在物理内存中,可能位于页面文件或尚未分配。访问这样的页会触发缺页异常(#PF),由IDT中的0xE号中断处理[^2]。 4. **缺页异常处理**:根据PTE的内容,缺页异常处理程序会采取不同的行动: - 如果PTE的1-9位和12-31位有值,说明页在页面文件中,则从页面文件读取数据到新的物理页,并更新PTE。 - 如果PTE全为0,则检查VAD(虚拟地址描述符),如果地址已分配则分配物理页,否则引发访问违例(0xC0000005)[^2]。 5. **物理页状态链表**:系统通过`MmPageLocationList`数组维护多个链表,每个链表对应一种物理页状态(如零化页、空闲页、备用页等)[^3]。 现在,用户希望分析MmPfnDatabase和MMPFN结构,以及页表项和内存错误问题。我们将分步骤进行: ### 步骤1:获取MmPfnDatabase地址 在WinDbg中执行: ```bash dd MmPfnDatabase ``` 输出示例(64位系统): ``` fffff800`04119228 fffffa80`00000000 000001f4`00000040 ... ``` 这里,`fffffa80`00000000`就是MmPfnDatabase的地址(注意:64位系统下,地址可能是64位,需要根据输出确定)[^4]。 ### 步骤2:解析MMPFN结构体 MMPFN结构体在WinDbg中可以通过`dt _MMPFN`查看定义。每个MMPFN结构体的大小可以通过`dt -v _MMPFN`查看(通常为48字节或28字节,取决于系统位数和版本)。 例如,查看第一个物理页(页帧号为0)的MMPFN结构体: ```bash !db fffffa80`00000000 # 查看第一个结构体的原始字节 dt _MMPFN fffffa80`00000000 # 解析结构体 ``` ### 步骤3:分析物理页状态 通过MMPFN结构体中的`u4.e1.PageLocation`字段可以获取页的状态(如`Active`、`Standby`等)。状态值对应的链表可通过`MmPageLocationList`数组获取[^3]。 查看`MmPageLocationList`: ```bash dd MmPageLocationList ``` 输出示例: ``` 83e5b894 83f7c000 83f7c040 83f7c080 83f7c0c0 ... ``` 这些地址是各个链表头的地址(如零化链表、空闲链表等)。 ### 步骤4:分析页表项(PTE) 给定一个线性地址,可以通过以下命令获取其PTE: ```bash !pte 线性地址 ``` 输出示例: ``` VA 00000000`00400000 PXE at FFFFF6FB7DBED000 PPE at FFFFF6FB7DBF0000 PDE at FFFFF6FB7E000000 PTE at FFFFF6FC00020000 contains 0000000000148063 contains 0000000000149063 contains 000000000014A063 contains 0000000000000000 pfn 148 ---DA--UWEV pfn 149 ---DA--UWEV pfn 14a ---DA--UWEV not valid ``` 如果PTE显示“not valid”(P=0),则需进一步分析: - 如果PTE的值非全0,则可能位于页面文件(如图a[^2])。 - 如果PTE全0,则检查VAD(使用`!vad`命令)以确定地址是否已分配。 ### 步骤5:处理内存错误(0xC0000005) 当发生访问违例(0xC0000005)时,通常是因为访问了未提交的地址(PTE全0且VAD中没有对应区域)[^2]。在调试中,可以通过以下步骤分析: 1. 获取出错时的线性地址(例如从异常记录中获取)。 2. 使用`!pte`查看该地址的PTE。 3. 如果PTE无效,使用`!vad`查看进程的VAD树,确认该地址是否已保留或提交。 ### 示例:分析一个位于页面文件中的PTE 假设线性地址0x400000的PTE为0xFFFFF6FC00020000,且显示为无效(P=0),但其他位非0: 1. 根据引用[2]中的图a,PTE的1-9位(页面文件号)和12-31位(页面文件偏移)有效。 2. 缺页异常处理程序会从页面文件(pagefile.sys)中读取该页,分配新的物理页,并更新PTE。 ### 关键公式 1. **物理页帧号(PFN)与物理地址转换**: $$ \text{物理地址} = \text{PFN} \times \text{页大小} + \text{页内偏移} $$ 在x86系统中,页大小通常为4096字节(0x1000)。 2. **PTE结构解析**(32位系统): - P位(位0):存在标志 - 页帧号(位12-31):当P=1时,指向物理页帧号 - 其他标志位(如R/W、U/S等) ### 注意事项 - 不同Windows版本中MMPFN结构可能有差异,需使用匹配的符号文件。 - 物理页状态链表(如MmZeroedPageListHead)可通过`!list`命令遍历。 §§相关问题§§ 1. 如何遍历MmPfnDatabase中的所有物理页帧? 2. 如何通过MMPFN结构判断一个物理页是否被共享? 3. 在缺页异常处理中,如何从页面文件恢复数据? 4. 如何修复由损坏的PTE导致的蓝屏错误?</think>### Windows内核调试:MMPFN结构与页表项(PTE)分析 #### 1. **MMPFN结构与页帧数据库** MMPFN(Modified Page Frame Number)结构体是Windows内存管理的核心数据结构,位于**页帧数据库(MmPfnDatabase)**中,包含所有物理页的状态信息[^1][^3][^4]。 **关键操作:** ```bash # 获取MmPfnDatabase地址 kd> dq MmPfnDatabase fffff800`04119228 fffffa80`00000000 # 此为数据库起始地址 # 查看第一个MMPFN结构体(48字节) kd> dt _MMPFN fffffa80`00000000 +0x000 u1 : __unnamed +0x008 PteAddress : 0xffffe000`00000000 _MMPTE +0x010 u2 : __unnamed +0x018 u3 : __unnamed +0x020 OriginalPte : _MMPTE +0x028 u4 : __unnamed ``` **MMPFN核心字段解析:** - **PteAddress**:指向该物理页对应的PTE - **u4.e1.PageLocation**:物理页状态(0=零化,1=空闲,2=备用,3=已修改等)[^3] - **OriginalPte**:原始页表项内容 - **u3.ShareCount**:共享计数(用于共享内存分析) #### 2. **物理页状态链表分析** 系统通过`MmPageLocationList`维护6种状态的物理页链表[^3]: ```bash kd> dd MmPageLocationList 83e5b894 83f7c000 # MmZeroedPageListHead(已清零) 83f7c040 # MmFreePageListHead(空闲页) 83f7c080 # MmStandbyPageListHead(备用页) 83f7c0c0 # MmModifiedPageListHead(已修改页) ... ``` **链表遍历示例(备用页):** ```bash kd> !list -t _MMPFN.u3.List -x "dt _MMPFN @$extret" 83f7c080 ``` #### 3. **页表项(PTE)与内存错误分析 PTE是虚拟地址到物理地址转换的关键结构,其状态决定内存访问行为[^2]: **PTE状态机分析:** ```bash # 查看虚拟地址的PTE kd> !pte 0x00400000 VA 00400000 PDE at C0600000 PTE at C0002000 contains 00000000`0400F867 contains 00000000`00000000 # P=0 表示无效 ``` **内存错误场景诊断:** | PTE状态 | 错误类型 | 处理机制 | 调试命令 | |----------------|---------------|---------------------------------|----------------------------| | `P=0`且全零 | 0xC0000005 | 检查VAD树[^2] | `!vad 地址` | | `P=0`非全零 | 页面文件回填 | 从pagefile.sys加载 | `!pte 地址; !pagefile` | | `P=1`但无权限 | 访问违例 | 检查PTE权限位 | `dt _MMPTE PTE地址` | | 转移状态(图c) | 软缺页 | 查询_MMPFN重建映射 | `!pfn 物理页号` | #### 4. **缺页异常(0xE)调试流程 当访问无效PTE时触发缺页异常: 1. 检查IDT 0xE条目:`!idt 0xE` 2. 分析异常上下文:`.cxr; !analyze -v` 3. 诊断PTE状态: ```bash kd> !error c0000005 # 解码访问违例 kd> .process /p 进程EPROCESS # 切到目标进程 kd> !pte 故障地址 ``` #### 5. **物理页与虚拟地址互查** ```bash # 虚拟地址→物理页帧号(PFN) kd> !vtop 0 虚拟地址 # PFN→MMPFN结构 kd> !pfn PFN编号 PFN 00000 at address FFFFFA8000000000 flink 00000001 blink / share count 00000001 pteaddress FFFFE00000000000 ``` ### 关键数据结构关系 ```mermaid graph LR A[虚拟地址] -->|!pte| B(PTE) B -->|P=1| C[物理页] B -->|P=0| D{PTE状态} D -->|全零| E[!vad查VAD树] D -->|非全零| F[页面文件回填] C -->|PFN| G[!pfn查MMPFN] G --> H[物理页状态链表] H -->|MmPageLocationList| I[零化/空闲/备用等] ``` ### 典型问题排查流程 1. 捕获访问违例(0xC0000005) 2. 获取故障地址:`kb`查看调用栈 3. 检查PTE状态:`!pte 故障地址` 4. 若PTE无效: - 全零:`!vad`检查地址是否提交 - 非全零:`!pagefile`检查页面文件 5. 若PTE有效: - 检查权限位:`dt _MMPTE PTE地址` - 检查物理页状态:`!pfn PFN`
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值