VAD树结构体的属性以及遍历
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
VAD树的属性以及遍历
前面学习过的PFNDATABSAE是管理物理页的,整个操作系统仅维护一个PFNDATABASE。
现在的VAD是管理虚拟内存的,每一个进程有自己单独的一个VAD树。
VAD树:
- 比如你使用VirtualAllocate函数申请一个内存,则会在VAD树上增加一个结点,其是_MMVAD结构体。
- 一个VAD结点可以有多个页,这在StartingVpn和EndingVpn会介绍。
- 当以MEN_SERVIED保留属性提交时,其只是在VAD树上挂上一个节点,真正提交时这棵树才是由意义的。
一、VAD结构体介绍
kd> dt _MMVAD
nt!_MMVAD
+0x000 StartingVpn : Uint4B
+0x004 EndingVpn : Uint4B
+0x008 Parent : Ptr32 _MMVAD
+0x00c LeftChild : Ptr32 _MMVAD
+0x010 RightChild : Ptr32 _MMVAD
+0x014 u : __unnamed
+0x018 ControlArea : Ptr32 _CONTROL_AREA
+0x01c FirstPrototypePte : Ptr32 _MMPTE
+0x020 LastContiguousPte : Ptr32 _MMPTE
+0x024 u2 : __unnamed
1. StringVpn 起始页 / EndingVpn结束页
1)两者算法是不同的。起始页:startingVpn*0x1000/结束页:EndVpn*0x1000+0xfff。
2. Parent、LeftChild、RightChild - 其父节点、左子树、右子树。
1)我们遍历这些树时用到的就是这些结构体成员。
3. u - 其是_MMVAN_FLAGS属性,非常重要的。
kd> dt _MMVAD_FLAGS
nt!_MMVAD_FLAGS
+0x000 CommitCharge : Pos 0, 19 Bits
+0x000 PhysicalMapping : Pos 19, 1 Bit
+0x000 ImageMap : Pos 20, 1 Bit
+0x000 UserPhysicalPages : Pos 21, 1 Bit
+0x000 NoChange : Pos 22, 1 Bit
+0x000 WriteWatch : Pos 23, 1 Bit
+0x000 Protection : Pos 24, 5 Bits
+0x000 LargePages : Pos 29, 1 Bit
+0x000 MemCommit : Pos 30, 1 Bit
+0x000 PrivateMemory : Pos 31, 1 Bit
1)CommitCharge 实际提交的页数。
其19Bits,我们内存低字节7ffffff,正好十九位。
比如我们以MEN_RESERVED保留形式提交了4页大小的内存,此时这里为2,将一页改为EXECUTE属性,这时这里就会变成2。
2)PyhsicalMapping:内核物理页映射。
3)UserPhysicalPages:内核物理页映射。
4)PrivateMemory:如果私有设置为1。
5)ImageMap:对dll/exe等文件进行保护,防止其被修改(使用映射写拷贝之类的原理)
如果ImageMap为1,PrivateMemory为0,说明其为DLL。
6)NoChange:关于锁页技术。当置为1,像VirtualProtect等函数不会改变其页的属性。
7)LargePage:标志是否为大页。
8)MemCommit:提交状态,只要提交就会置为1(CommitCharge存储提交了多少页)
9)Protection:3bit,关于保护(比如页的读写、可执行等)。
下面介绍了其对应相关文件的对应关系,看这个很好理解。

这篇博客详细介绍了R3与R0中页面保护的对应关系:R3环申请内存时页面保护与_MMVAD_FLAGS位的对应关系
4. ContraArea 控制结构 :
其指向一个_CONTROL_AREA的数据结构,该结构就暂不表述了。
1)_CONTROL_AREA+ 0x24 FilePointer,文件指针,指向一个_FILE_OBJECT结构体。
2)_FILE_OBJECT结构体中,保存着文件对象很多关键的信息。
a> +0x30 FileName 文件名
若想知道该页属于哪个文件,可以查看这里。
将.sys文件伪装成.dll文件,则必须修改这里。
b> +0x26-0x28 文件保护属性
比如一个文件被独占无法删除,在内核中你可以将DeleteAccess位置1,之后强制删除。
二、利用windbg遍历VAD树
1. 每个进程的VAD树存储在_EPROCESS+0x11c结构体中,其是ROOTVAD根结点。

2. 获取每个进程的 _EPROCESS,使用指令!process 0 0,得到PROCESS的值就是指向_EPROCESS的指针。

3. 当获取VadRoot之后,可以使用 !vad VadRoot来显示该进程的VAD树。

三、使用代码实现VAD树的遍历
代码核心就是先遍历进程找出目标进程(这里默认 test.exe),之后对目标进程的VAD树进行遍历。
#include <ntddk.h> //---------------------//
// MMVAD结构体简单定义 //
//---------------------//
typedef struct _MMVAD {
ULONG StartingVpn;
ULONG EndingVpn;
struct _MMVAD * Parent;
struct _MMVAD * LeftChild;
struct _MMVAD * RightChild;
}MMVAD,*PMMVAD; VOID Unload(IN PDRIVER_OBJECT pDriverObject) {
DbgPrint("Driver UnLoad!");
} //-----------//
// 遍历VAD树 //
//-----------//
VOID vad_enum(PMMVAD pVad) {
if (pVad) {
DbgPrint("Start: %x | End: %x | \r\n", pVad->StartingVpn, pVad->EndingVpn);
if (pVad->LeftChild)
vad_enum(pVad->LeftChild);
if (pVad->RightChild)
vad_enum(pVad->RightChild);
}
} //-------------------------------------------------------------//
// 在内核中进程遍历的原理就是先获取系统进程EPROCESS结构 //
// 然后依照其链表来获取其他的进程 //
// 依次遍历出来 //
//-------------------------------------------------------------//
NTSTATUS process_enum() { PEPROCESS pEprocess = NULL; // 得到系统进程地址
PEPROCESS pFirstEprocess = NULL;
ULONG ulProcessName = ; // 字符串指针,指向进程名称
ULONG ulProcessID = ; // 进程ID
ANSI_STRING target_str; // 带检测进程的名称
ANSI_STRING ansi_string; //
ULONG VadRoot; //----------------------------//
// 得到当前系统进程的EPROCESS //
//----------------------------//
pEprocess = PsGetCurrentProcess();
if (pEprocess == NULL) {
DbgPrint("获取当前系统进程EPROCESS错误..");
return STATUS_SUCCESS;
}
DbgPrint("pEprocess addr is %x0x8\r\n", pEprocess);
pFirstEprocess = pEprocess; while (pEprocess) { ulProcessName = (ULONG)pEprocess + 0x174;
ulProcessID = *(ULONG*)((ULONG)pEprocess + 0x84);
VadRoot = *(ULONG*)((ULONG)pEprocess + 0x11c); //--------------------------------------//
// 将目标进程与当前进程的进程名进行对比 //
//--------------------------------------//
RtlInitAnsiString(&ansi_string, (PCSTR)ulProcessName);
RtlInitAnsiString(&target_str, "test.exe");
if (RtlEqualString(&ansi_string, &target_str, TRUE)) {
DbgPrint("检测到进程字符串,%x", ulProcessID);
vad_enum((PMMVAD)VadRoot); // 开始遍历目标进程的VAD树
return STATUS_SUCCESS;
}
pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess + 0x88) - 0x88); if (pEprocess == pFirstEprocess || *(ULONG*)((ULONG)pEprocess + 0x84) <= ) {
DbgPrint("遍历结束!未检测到进程ID!\r\n");
break;
}
}
return STATUS_SUCCESS;
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING registeryPat) {
DbgPrint("Driver Loaded!");
pDriverObject->DriverUnload = Unload;
process_enum();
return STATUS_SUCCESS;
}
VAD树结构体的属性以及遍历的更多相关文章
- 驱动开发:内核遍历进程VAD结构体
在上一篇文章<驱动开发:内核中实现Dump进程转储>中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descrip ...
- [转] JavaScript中的属性:如何遍历属性
在JavaScript中,遍历一个对象的属性往往没有在其他语言中遍历一个哈希(有些语言称为字典)的键那么简单.这主要有两个方面的原因:一个是,JavaScript中的对象通常都处在某个原型链中,它会从 ...
- 【转载】JavaScript中的属性:如何遍历属性
转载自:http://www.cnblogs.com/ziyunfei/archive/2012/11/03/2752905.html 在JavaScript中,遍历一个对象的属性往往没有在其他语言中 ...
- JavaScript中对象的属性:如何遍历属性
for/in 语句循环遍历对象的属性. js中获取key得到某对象中相对应的value的方法:obj.key js中根据动态key得到某对象中相对应的value的方法有二: 一.var key = & ...
- js 属性的遍历
引自:http://es6.ruanyifeng.com/#docs/object 属性的遍历 ES6 一共有5种方法可以遍历对象的属性. (1)for...in for...in循环遍历对象自身的和 ...
- 5.Swift枚举|结构体|类|属性|方法|下标脚本|继承
1. 枚举: ->在Swift中依然适用整数来标示枚举值,需搭配case关键字 enum Celebrity{ case DongXie,XiDu,Nandi,BeiGai } // 从左 ...
- Swift----函数 、 闭包 、 枚举 、 类和结构体 、 属性
1 数组排序 1.1 问题 本案例实现一个整型数组排序的函数,数组排序的规则由传递的规则函数决定. 1.2 方案 首先定义一个整型数组排序函数sortInts,该函数有一个整型数组类型的参数,该参数必 ...
- Iterator遍历器 调用Symbol.Iterator属性,遍历器对象。
Iterator实现原理 创建一个指针对象,指向当前数据结构的起始位置.也就是说,遍历器对象本质上,就是一个指针对象. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员. 第二次调 ...
- C语言 结构体中属性的偏移量计算
//计算结构体偏移量 #include<stdio.h> #include<stdlib.h> #include<string.h> //详解:对于offscfof ...
随机推荐
- Netty源码分析 (十一)----- 拆包器之LengthFieldBasedFrameDecoder
本篇文章主要是介绍使用LengthFieldBasedFrameDecoder解码器自定义协议.通常,协议的格式如下: LengthFieldBasedFrameDecoder是netty解决拆包粘包 ...
- 使用Consul做leader选举的方案
在分布式集群部署模式下,为了维护数据一致性,通常需要选举出一个leader来进行协调,并且在leader挂掉后能从集群中选举出一个新的leader.选举leader的方案有很多种,对Paxos和Raf ...
- Java线程池的拒绝策略
一.简介 jdk1.5 版本新增了JUC并发编程包,极大的简化了传统的多线程开发.前面文章中介绍了线程池的使用,链接地址:https://www.cnblogs.com/eric-fang/p/900 ...
- asp.net core IdentityServer4 概述
概览 现代应用程序看上去大都是这样的: 最常见的交互是: 浏览器与Web应用程序通信 Web应用程序与Web API通信(有时是独立的,有时是代表用户的) 基于浏览器的应用程序与Web API通信 本 ...
- select2 分组后的选项无法被选中
在使用select2组件的过程中发现分组下的选项无法选中,与分组在同一级的选项可以被选中! 1.所用select2版本select2-4.0.32 2.HTML代码: <input id=&qu ...
- 我用阿里云的虚拟云主机,也能配置https加密吗?
我用阿里云的虚拟云主机,也能配置https加密吗?答案是YES. 整个过程比想象中还要简单,都是一些基本的配置,虚拟主机 Web托管都可以很容易的搞定https. 首先我们要了解一下,阿里云是怎么支持 ...
- java8 函数接口
[前言] java8新特性 java8 Optional使用总结 java8 lambda表达式 Java 8 时间日期使用 1.函数式接口新特性 java8中引入了函数式接口新特性,使用@Funct ...
- .Net Core 商城微服务项目系列(八):购物车
最近加班有点多,一周五天,四天加班到11点+,心很累.原因是我当前在的这个组比较特殊,相当于业务的架构组,要为其它的开发组提供服务和监控.所以最近更新的也少,不过这个元旦三天假应该会更新三篇. 这篇是 ...
- Spring Boot (八): Mybatis 增强工具 MyBatis-Plus
1. 简介 在上一篇文章<Spring Boot (七): Mybatis极简配置> 中我们介绍了在 Spring Boot 中 Mybatis 的基础使用方式,其中有一部分美中不足的是 ...
- 最近太多人问Protobuf的问题了,把这个重新搬出来!
pb杀手 我先让pbkiller做个自我介绍 pbkiller:我是一位专业的争对 protobuf 问题训练有素的杀手,我可以为您轻松搞定 protobuf 在 Cocos Creaotr 开发中的 ...