在上一篇文章《驱动开发:内核中实现Dump进程转储》
中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD
结构,该结构的全程是Virtual Address Descriptor
即虚拟地址描述符
,VAD是一个AVL
自平衡二叉树
,树的每一个节点代表一段虚拟地址空间。程序中的代码段,数据段,堆段都会各种占用一个或多个VAD
节点,由一个MMVAD
结构完整描述。
VAD结构的遍历效果如下:

那么这个结构在哪?每一个进程都有自己单独的VAD
结构树,这个结构通常在EPROCESS
结构里面里面,在内核调试模式下使用dt _EPROCESS
可得到如下信息。
lyshark.com 1: kd> dt _EPROCESS ntdll!_EPROCESS +0x500 Vm : _MMSUPPORT_FULL +0x640 MmProcessLinks : _LIST_ENTRY +0x650 ModifiedPageCount : Uint4B +0x654 ExitStatus : Int4B +0x658 VadRoot : _RTL_AVL_TREE +0x660 VadHint : Ptr64 Void +0x668 VadCount : Uint8B +0x670 VadPhysicalPages : Uint8B +0x678 VadPhysicalPagesLimit : Uint8B
|
可以看到在本系统中VAD的偏移是+0x658
紧跟其后的还有vadCount
的计数等。

VAD结构是如何被添加的?通常情况下系统调用VirtualAllocate
等申请一段堆内存时,则会在VAD树上增加一个结点_MMVAD
结构体,需要说明的是栈并不受VAD的管理。由系统直接分配空间,并把地址记录在了TEB中。
lyshark.com 0: kd> dt _MMVAD nt!_MMVAD +0x000 Core : _MMVAD_SHORT +0x040 u2 : <anonymous-tag> +0x048 Subsection : Ptr64 _SUBSECTION +0x050 FirstPrototypePte : Ptr64 _MMPTE +0x058 LastContiguousPte : Ptr64 _MMPTE +0x060 ViewLinks : _LIST_ENTRY +0x070 VadsProcess : Ptr64 _EPROCESS +0x078 u4 : <anonymous-tag> +0x080 FileObject : Ptr64 _FILE_OBJECT
|
结构体MMVAD
则是每一个VAD
内存块的属性,这个内存结构定义在WinDBG中可看到。

如上在EPROCESS
结构中可以找到VAD结构的相对偏移+0x658
以及进程VAD计数偏移+0x668
,我们首先通过!process 0 0
指令得到当前所有进程的EPROCESS
结构,并选中进程。
lyshark.com 0: kd> !process 0 0 PROCESS ffffe28fbb0860c0 SessionId: 1 Cid: 11a8 Peb: 0035c000 ParentCid: 11c8 DirBase: 309f3002 ObjectTable: ffffac87ba3da580 HandleCount: 145. Image: x64.exe
|
此处的ffffe28fbb0860c0
正是我们所需要的EPROCESS
结构。

当需要得到该进程的VAD结构时,只需要使用!vad ffffe28fbb0860c0 + 0x658
来显示该进程的VAD树。

至于获取VAD有多少条,则可以直接使用!vad ffffe28fbb0860c0 + 0x668
来获取到。

既然手动可以遍历出来,那么自动化也并不难,首先定义头文件vad.h
同样这是微软定义,如果想要的到最新的,自己下载WinDBG调试内核输入命令。
#pragma once #include <ntifs.h>
typedef struct _MM_GRAPHICS_VAD_FLAGS // 15 elements, 0x4 bytes (sizeof) { ULONG32 Lock : 1; ULONG32 LockContended : 1; ULONG32 DeleteInProgress : 1; ULONG32 NoChange : 1; ULONG32 VadType : 3; ULONG32 Protection : 5; ULONG32 PreferredNode : 6; ULONG32 PageSize : 2; ULONG32 PrivateMemoryAlwaysSet : 1; ULONG32 WriteWatch : 1; ULONG32 FixedLargePageSize : 1; ULONG32 ZeroFillPagesOptional : 1; ULONG32 GraphicsAlwaysSet : 1; ULONG32 GraphicsUseCoherentBus : 1; ULONG32 GraphicsPageProtection : 3; }MM_GRAPHICS_VAD_FLAGS, *PMM_GRAPHICS_VAD_FLAGS; typedef struct _MM_PRIVATE_VAD_FLAGS // 15 elements, 0x4 bytes (sizeof) { ULONG32 Lock : 1; ULONG32 LockContended : 1; ULONG32 DeleteInProgress : 1; ULONG32 NoChange : 1; ULONG32 VadType : 3; ULONG32 Protection : 5; ULONG32 PreferredNode : 6; ULONG32 PageSize : 2; ULONG32 PrivateMemoryAlwaysSet : 1; ULONG32 WriteWatch : 1; ULONG32 FixedLargePageSize : 1; ULONG32 ZeroFillPagesOptional : 1; ULONG32 Graphics : 1; ULONG32 Enclave : 1; ULONG32 ShadowStack : 1; }MM_PRIVATE_VAD_FLAGS, *PMM_PRIVATE_VAD_FLAGS;
typedef struct _MMVAD_FLAGS // 9 elements, 0x4 bytes (sizeof) { ULONG32 Lock : 1; ULONG32 LockContended : 1; ULONG32 DeleteInProgress : 1; ULONG32 NoChange : 1; ULONG32 VadType : 3; ULONG32 Protection : 5; ULONG32 PreferredNode : 6; ULONG32 PageSize : 2; ULONG32 PrivateMemory : 1; }MMVAD_FLAGS, *PMMVAD_FLAGS;
typedef struct _MM_SHARED_VAD_FLAGS // 11 elements, 0x4 bytes (sizeof) { ULONG32 Lock : 1; ULONG32 LockContended : 1; ULONG32 DeleteInProgress : 1; ULONG32 NoChange : 1; ULONG32 VadType : 3; ULONG32 Protection : 5; ULONG32 PreferredNode : 6; ULONG32 PageSize : 2; ULONG32 PrivateMemoryAlwaysClear : 1; ULONG32 PrivateFixup : 1; ULONG32 HotPatchAllowed : 1; }MM_SHARED_VAD_FLAGS, *PMM_SHARED_VAD_FLAGS;
typedef struct _MMVAD_FLAGS2 // 7 elements, 0x4 bytes (sizeof) { ULONG32 FileOffset : 24; ULONG32 Large : 1; ULONG32 TrimBehind : 1; ULONG32 Inherit : 1; ULONG32 NoValidationNeeded : 1; ULONG32 PrivateDemandZero : 1; ULONG32 Spare : 3; }MMVAD_FLAGS2, *PMMVAD_FLAGS2;
typedef struct _MMVAD_SHORT { RTL_BALANCED_NODE VadNode; UINT32 StartingVpn; UINT32 EndingVpn; UCHAR StartingVpnHigh; UCHAR EndingVpnHigh; UCHAR CommitChargeHigh; UCHAR SpareNT64VadUChar; INT32 ReferenceCount; EX_PUSH_LOCK PushLock; struct { union { ULONG_PTR flag; MM_PRIVATE_VAD_FLAGS PrivateVadFlags; MMVAD_FLAGS VadFlags; MM_GRAPHICS_VAD_FLAGS GraphicsVadFlags; MM_SHARED_VAD_FLAGS SharedVadFlags; }Flags;
}u1;
PVOID EventList;
}MMVAD_SHORT, *PMMVAD_SHORT;
typedef struct _MMADDRESS_NODE { ULONG64 u1; struct _MMADDRESS_NODE* LeftChild; struct _MMADDRESS_NODE* RightChild; ULONG64 StartingVpn; ULONG64 EndingVpn; }MMADDRESS_NODE, *PMMADDRESS_NODE;
typedef struct _MMEXTEND_INFO // 2 elements, 0x10 bytes (sizeof) { UINT64 CommittedSize; ULONG32 ReferenceCount; UINT8 _PADDING0_[0x4]; }MMEXTEND_INFO, *PMMEXTEND_INFO; struct _SEGMENT { struct _CONTROL_AREA* ControlArea; ULONG TotalNumberOfPtes; ULONG SegmentFlags; ULONG64 NumberOfCommittedPages; ULONG64 SizeOfSegment; union { struct _MMEXTEND_INFO* ExtendInfo; void* BasedAddress; }u; ULONG64 SegmentLock; ULONG64 u1; ULONG64 u2; PVOID* PrototypePte; ULONGLONG ThePtes[0x1]; };
typedef struct _EX_FAST_REF { union { PVOID Object; ULONG_PTR RefCnt : 3; ULONG_PTR Value; }; } EX_FAST_REF, *PEX_FAST_REF;
typedef struct _CONTROL_AREA // 17 elements, 0x80 bytes (sizeof) { struct _SEGMENT* Segment; union // 2 elements, 0x10 bytes (sizeof) { struct _LIST_ENTRY ListHead; VOID* AweContext; }; UINT64 NumberOfSectionReferences; UINT64 NumberOfPfnReferences; UINT64 NumberOfMappedViews; UINT64 NumberOfUserReferences; ULONG32 u; ULONG32 u1; struct _EX_FAST_REF FilePointer; }CONTROL_AREA, *PCONTROL_AREA;
typedef struct _SUBSECTION_ { struct _CONTROL_AREA* ControlArea;
}SUBSECTION, *PSUBSECTION;
typedef struct _MMVAD { MMVAD_SHORT Core; union /*0x040*/ { UINT32 LongFlags2; MMVAD_FLAGS2 VadFlags2;
}u2; PSUBSECTION Subsection; PVOID FirstPrototypePte; PVOID LastContiguousPte; LIST_ENTRY ViewLinks; PEPROCESS VadsProcess; PVOID u4; PVOID FileObject; }MMVAD, *PMMVAD;
typedef struct _RTL_AVL_TREE // 1 elements, 0x8 bytes (sizeof) { struct _RTL_BALANCED_NODE* Root; }RTL_AVL_TREE, *PRTL_AVL_TREE;
typedef struct _VAD_INFO_ { ULONG_PTR pVad; ULONG_PTR startVpn; ULONG_PTR endVpn; ULONG_PTR pFileObject; ULONG_PTR flags; }VAD_INFO, *PVAD_INFO;
typedef struct _ALL_VADS_ { ULONG nCnt; VAD_INFO VadInfos[1]; }ALL_VADS, *PALL_VADS;
typedef struct _MMSECTION_FLAGS // 27 elements, 0x4 bytes (sizeof) { UINT32 BeingDeleted : 1; UINT32 BeingCreated : 1; UINT32 BeingPurged : 1; UINT32 NoModifiedWriting : 1; UINT32 FailAllIo : 1; UINT32 Image : 1; UINT32 Based : 1; UINT32 File : 1; UINT32 AttemptingDelete : 1; UINT32 PrefetchCreated : 1; UINT32 PhysicalMemory : 1; UINT32 ImageControlAreaOnRemovableMedia : 1; UINT32 Reserve : 1; UINT32 Commit : 1; UINT32 NoChange : 1; UINT32 WasPurged : 1; UINT32 UserReference : 1; UINT32 GlobalMemory : 1; UINT32 DeleteOnClose : 1; UINT32 FilePointerNull : 1; ULONG32 PreferredNode : 6; UINT32 GlobalOnlyPerSession : 1; UINT32 UserWritable : 1; UINT32 SystemVaAllocated : 1; UINT32 PreferredFsCompressionBoundary : 1; UINT32 UsingFileExtents : 1; UINT32 PageSize64K : 1; }MMSECTION_FLAGS, *PMMSECTION_FLAGS;
typedef struct _SECTION // 9 elements, 0x40 bytes (sizeof) { struct _RTL_BALANCED_NODE SectionNode; UINT64 StartingVpn; UINT64 EndingVpn; union { PCONTROL_AREA ControlArea; PVOID FileObject;
}u1; UINT64 SizeOfSection; union { ULONG32 LongFlags; MMSECTION_FLAGS Flags; }u; struct // 3 elements, 0x4 bytes (sizeof) { ULONG32 InitialPageProtection : 12; ULONG32 SessionId : 19; ULONG32 NoValidationNeeded : 1; }; }SECTION, *PSECTION;
|
引入vad.h
头文件,并写入如下代码,此处的eprocess_offset_VadRoot
以及eprocess_offset_VadCount
则是上方得出的相对于EPROCESS
结构的偏移值,每个系统都不一样,版本不同偏移值会不同。
#include "vad.h" #include <ntifs.h>
#define eprocess_offset_VadRoot 0x658 #define eprocess_offset_VadCount 0x668
VOID EnumVad(PMMVAD Root, PALL_VADS pBuffer, ULONG nCnt) { if (!Root || !pBuffer || !nCnt) { return; }
__try { if (nCnt > pBuffer->nCnt) { ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh; endptr = endptr << 32;
ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh; startptr = startptr << 32;
pBuffer->VadInfos[pBuffer->nCnt].pVad = (ULONG_PTR)Root;
pBuffer->VadInfos[pBuffer->nCnt].startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;
pBuffer->VadInfos[pBuffer->nCnt].endVpn = ((endptr | Root->Core.EndingVpn) << PAGE_SHIFT) + 0xfff;
pBuffer->VadInfos[pBuffer->nCnt].flags = Root->Core.u1.Flags.flag;
if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea)) { if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4))) { pBuffer->VadInfos[pBuffer->nCnt].pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4); } } pBuffer->nCnt++; }
if (MmIsAddressValid(Root->Core.VadNode.Left)) { EnumVad((PMMVAD)Root->Core.VadNode.Left, pBuffer, nCnt); }
if (MmIsAddressValid(Root->Core.VadNode.Right)) { EnumVad((PMMVAD)Root->Core.VadNode.Right, pBuffer, nCnt); } } __except (1) { } }
BOOLEAN EnumProcessVad(ULONG Pid, PALL_VADS pBuffer, ULONG nCnt) { PEPROCESS Peprocess = 0; PRTL_AVL_TREE Table = NULL; PMMVAD Root = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)Pid, &Peprocess))) { Table = (PRTL_AVL_TREE)((UCHAR*)Peprocess + eprocess_offset_VadRoot); if (!MmIsAddressValid(Table) || !eprocess_offset_VadRoot) { return FALSE; }
__try { Root = (PMMVAD)Table->Root;
if (nCnt > pBuffer->nCnt) { ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh; endptr = endptr << 32;
ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh; startptr = startptr << 32;
pBuffer->VadInfos[pBuffer->nCnt].pVad = (ULONG_PTR)Root;
pBuffer->VadInfos[pBuffer->nCnt].startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;
pBuffer->VadInfos[pBuffer->nCnt].endVpn = (endptr | Root->Core.EndingVpn) << PAGE_SHIFT; pBuffer->VadInfos[pBuffer->nCnt].flags = Root->Core.u1.Flags.flag;
if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea)) { if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4))) { pBuffer->VadInfos[pBuffer->nCnt].pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4); } } pBuffer->nCnt++; }
if (Table->Root->Left) { EnumVad((MMVAD*)Table->Root->Left, pBuffer, nCnt); }
if (Table->Root->Right) { EnumVad((MMVAD*)Table->Root->Right, pBuffer, nCnt); } } __finally { ObDereferenceObject(Peprocess); } } else { return FALSE; }
return TRUE; }
VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark \n"));
typedef struct { ULONG nPid; ULONG nSize; PALL_VADS pBuffer; }VADProcess;
__try { VADProcess vad = { 0 };
vad.nPid = 4520;
vad.nSize = sizeof(VAD_INFO) * 0x5000 + sizeof(ULONG);
vad.pBuffer = (PALL_VADS)ExAllocatePool(PagedPool, vad.nSize);
ULONG nCount = (vad.nSize - sizeof(ULONG)) / sizeof(VAD_INFO);
EnumProcessVad(vad.nPid, vad.pBuffer, nCount);
for (size_t i = 0; i < vad.pBuffer->nCnt; i++) { DbgPrint("StartVPN = %p | ", vad.pBuffer->VadInfos[i].startVpn); DbgPrint("EndVPN = %p | ", vad.pBuffer->VadInfos[i].endVpn); DbgPrint("PVAD = %p | ", vad.pBuffer->VadInfos[i].pVad); DbgPrint("Flags = %d | ", vad.pBuffer->VadInfos[i].flags); DbgPrint("pFileObject = %p \n", vad.pBuffer->VadInfos[i].pFileObject); } } __except (1) { }
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|
程序运行后输出效果如下:
