Windows 虚拟内存管理

文章目录

  • 虚拟内存管理
    • 1. 基本管理
    • 2. 虚拟内存管理结构
    • 3. Windbg查看VAD
    • 4. 原理
      • 4.1 NtAllocateVirtualMemory
      • 4.2 MiProtectVirtualMemory
    • 5. 应用
      • 5.1 模块枚举
      • 5.2 隐藏进程模块
      • 5.3 内存隐藏

虚拟内存管理

1. 基本管理

对于内存管理的方式有很多,内存也分为很多种,例如:

  1. 堆内存
  2. 栈内存

对于内存的分配函数也分为很多种,例如:new,malloc,Alloc, HeapAlloc;对于这些内存管理函数,其实都是封装了底层内存管理,然后对上层提供了相关的内存接口,最原始的内存管理函数应该是分为如下几个:

//分配内存
LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);

//释放内存
BOOL WINAPI VirtualFreeEx(
  _In_ HANDLE hProcess,
  _In_ LPVOID lpAddress,
  _In_ SIZE_T dwSize,
  _In_ DWORD  dwFreeType
);

//修改内存的属性
BOOL WINAPI VirtualProtect(
  _In_  LPVOID lpAddress,
  _In_  SIZE_T dwSize,
  _In_  DWORD  flNewProtect,
  _Out_ PDWORD lpflOldProtect
);

//查询内存属性信息
SIZE_T WINAPI VirtualQueryEx(
  _In_     HANDLE                    hProcess,
  _In_opt_ LPCVOID                   lpAddress,
  _Out_    PMEMORY_BASIC_INFORMATION lpBuffer,
  _In_     SIZE_T                    dwLength
);

所有的上层内存函数,底层都是通过这些函数分配一个大块内存,然后在底层中,通过管理向上层提供小块内存操作接口。

2. 虚拟内存管理结构

在windows中,虚拟内存是针对进程的,每个进程的虚拟内存是不同的,因此在进程结构中存在管理虚拟内存的变量VadRoot,如下:

0: kd> dt  nt!_EPROCESS ffff9c84`80b133c0 -n VadRoot
nt!_EPROCESS
   +0x658 VadRoot : _RTL_AVL_TREE

对于VadRoot的结构,每个系统的版本都存在不同,WRK中的结构信息如下:

typedef struct _MMVAD {
    union {
        LONG_PTR Balance : 2;
        struct _MMVAD *Parent;
    } u1;
    struct _MMVAD *LeftChild;
    struct _MMVAD *RightChild;
    ULONG_PTR StartingVpn;
    ULONG_PTR EndingVpn;

    union {
        ULONG_PTR LongFlags;
        MMVAD_FLAGS VadFlags;
    } u;
    PCONTROL_AREA ControlArea;
    PMMPTE FirstPrototypePte;
    PMMPTE LastContiguousPte;
    union {
        ULONG LongFlags2;
        MMVAD_FLAGS2 VadFlags2;
    } u2;
} MMVAD, *PMMVAD;

这个是一个RVL树结构:

  1. StringVpn 起始页 / EndingVpn结束页;两者算法是不同的。起始页:startingVpn0x1000/结束页:EndVpn0x1000+0xfff。
  2. Parent、LeftChild、RightChild - 其父节点、左子树、右子树。
  3. VadFlags属性信息,如下:
typedef struct _MMVAD_FLAGS {
    ULONG_PTR CommitCharge : COMMIT_SIZE; // limits system to 4k pages or bigger!
    ULONG_PTR NoChange : 1;
    ULONG_PTR VadType : 3;
    ULONG_PTR MemCommit: 1;
    ULONG_PTR Protection : 5;
    ULONG_PTR Spare : 2;
    ULONG_PTR PrivateMemory : 1;    // used to tell VAD from VAD_SHORT
} MMVAD_FLAGS;
  1. ContraArea 控制结构,如下:
typedef struct _CONTROL_AREA {
    PSEGMENT Segment;
    LIST_ENTRY DereferenceList;
    ULONG NumberOfSectionReferences;    // All section refs & image flushes
    ULONG NumberOfPfnReferences;        // valid + transition prototype PTEs
    ULONG NumberOfMappedViews;          // total # mapped views, including
                                        // system cache & system space views
    ULONG NumberOfSystemCacheViews;     // system cache views only
    ULONG NumberOfUserReferences;       // user section & view references
    union {
        ULONG LongFlags;
        MMSECTION_FLAGS Flags;
    } u;
    PFILE_OBJECT FilePointer;    //文件对象,例如dll,sys等等
    PEVENT_COUNTER WaitingForDeletion;
    USHORT ModifiedWriteCount;
    USHORT FlushInProgressCount;
    ULONG WritableUserReferences;
#if !defined (_WIN64)
    ULONG QuadwordPad;
#endif
} CONTROL_AREA, *PCONTROL_AREA;

我们知道,对于一个进程,所有的模块都会被映射到内存空间中,所以遍历VadRoot中的CONTROL_AREA文件对象就可以遍历到所有加载的模块信息了

如果我们希望别人看不到我们的内存,那么我们隐藏VadRoot中的结构就可以到达目的。

3. Windbg查看VAD

在windbg中,提供了!vad命令来查看vad内存信息,查看结构如下:

0: kd> dt nt!_EPROCESS ffff9c84`af3980c0 -n VadRoot
   +0x658 VadRoot : _RTL_AVL_TREE
0: kd> dx -id 0,0,ffff9c8480b133c0 -r1 (*((ntkrnlmp!_RTL_AVL_TREE *)0xffff9c84af398718))
(*((ntkrnlmp!_RTL_AVL_TREE *)0xffff9c84af398718))                 [Type: _RTL_AVL_TREE]
    [+0x000] Root             : 0xffff9c84b16d61c0 [Type: _RTL_BALANCED_NODE *]

0: kd> !vad 0xffff9c84af398718
VAD             Level         Start             End              Commit
ffff9c84b1ac5770  7           7ffe0           7ffe0               1 Private      READONLY           
ffff9c84b1ac58b0  6           7ffe6           7ffe6               1 Private      READONLY           
ffff9c84b1ac5220  8         72ae870         72ae8ef              11 Private      READWRITE          
ffff9c84aaf468e0  7         72ae8f0         72ae96f              11 Private      READWRITE          
ffff9c84b1ac5a90  5         72aea00         72aebff               5 Private      READWRITE          
ffff9c84a8a5d0c0  7        1eeb0400        1eeb040f               0 Mapped       READWRITE          Pagefile section, shared commit 0
ffff9c84a8a5d980  8        1eeb0410        1eeb0417               0 Mapped       READWRITE          Pagefile section, shared commit 0
ffff9c84ac05e4f0  6        1eeb0420        1eeb043a               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c849b3440d0  7        1eeb0440        1eeb0443               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c84b1ac5360  4        1eeb0450        1eeb0451               2 Private      READWRITE          
ffff9c84af58e410  8        1eeb0460        1eeb0491               4 Private      READWRITE          
ffff9c84af591b60  7        1eeb04a0        1eeb04d1               1 Private      READWRITE          
ffff9c84a8a5d700  8        1eeb04e0        1eeb04e0               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c84a8a5d7a0  6        1eeb04f0        1eeb04f0               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c84a8a5d840  8        1eeb0500        1eeb050d               0 Mapped       READONLY           \Windows\servicing\CbsMsg.dll
ffff9c84af58a0e0  7        1eeb0510        1eeb060f             255 Private      READWRITE          
ffff9c84a8a5cc60  5        1eeb0610        1eeb06d6               0 Mapped       READONLY           \Windows\System32\locale.nls
ffff9c84a8a5ea60  9        1eeb06e0        1eeb06e7               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c84a8a5ec40  8        1eeb06f0        1eeb07b0               0 Mapped       READONLY           Pagefile section, shared commit 0
ffff9c84af597ba0  7        1eeb07c0        1eeb07cf               8 Private      READWRITE          
ffff9c84b16d7b10  9        1eeb07d0        1eeb07df               9 Private      READWRITE          
//...

ffff9c84a8a5e2e0  9       7fff30380       7fff3039e               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\profapi.dll
ffff9c84a8a5dde0  6       7fff303a0       7fff303b0               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\kernel.appcore.dll
ffff9c84a8a5eba0  9       7fff303c0       7fff30409               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\powrprof.dll
ffff9c84a8a5fbe0  8       7fff30410       7fff305a3               8 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\gdi32full.dll
ffff9c84a8a5ce40  7       7fff305b0       7fff3064d               7 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\msvcp_win.dll
ffff9c84a8a5c9e0  8       7fff30650       7fff30749               4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ucrtbase.dll
ffff9c84a8a5d200  4       7fff30750       7fff309f2               9 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\KernelBase.dll
ffff9c84a8a5dac0  8       7fff30a00       7fff30b48              10 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\crypt32.dll
ffff9c84a8a5e9c0  9       7fff30b50       7fff30b70               2 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\win32u.dll
ffff9c84a8a5e240  7       7fff30b80       7fff30bdb               4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\wintrust.dll
ffff9c84a8a5e560  8       7fff30be0       7fff30c29               5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\cfgmgr32.dll
ffff9c84a8a5cd00  6       7fff30c30       7fff30caf               2 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\bcryptprimitives.dll
ffff9c84a8a5e060  8       7fff30d60       7fff30d76               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\cryptsp.dll
ffff9c84a8a5db60  9       7fff30d80       7fff30da5               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\bcrypt.dll
ffff9c84a8a5d160  7       7fff31590       7fff31626               6 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\sechost.dll
ffff9c84a8a5d3e0  9       7fff31630       7fff316cd              10 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\msvcrt.dll
ffff9c84a8a5f6e0  8       7fff316d0       7fff31863               6 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\user32.dll
ffff9c84a8a5ca80  9       7fff31900       7fff31a1f               5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\rpcrt4.dll
ffff9c84a8a5c8a0  5       7fff31bd0       7fff31c81               5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\kernel32.dll
ffff9c84a8a5c300  7       7fff31c90       7fff31fc5               9 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\combase.dll
ffff9c84a8a5e7e0  8       7fff31fd0       7fff32072               8 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\advapi32.dll
ffff9c84a8a5d2a0  6       7fff32110       7fff321d3               6 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\oleaut32.dll
ffff9c84a8a5e600  8       7fff32550       7fff326a5               6 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ole32.dll
ffff9c84a8a5e6a0  9       7fff32da0       7fff32dc5               4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\gdi32.dll
ffff9c84a8a5e740  7       7fff332c0       7fff33361               9 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\clbcatq.dll
ffff9c84a8a5fdc0  9       7fff33370       7fff333de               4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ws2_32.dll
ffff9c84ac05d5f0  8       7fff33480       7fff3366f              17 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ntdll.dll

4. 原理

4.1 NtAllocateVirtualMemory

我们调用VirtualAlloc分配内存的时候,底层就会通过NtAllocateVirtualMemory分配虚拟内存,这个函数的基本流程如下:

NTSTATUS
NtAllocateVirtualMemory(
    __in HANDLE ProcessHandle,
    __inout PVOID *BaseAddress,
    __in ULONG_PTR ZeroBits,
    __inout PSIZE_T RegionSize,
    __in ULONG AllocationType,
    __in ULONG Protect
    )
{
    //...
    PMMVAD Vad;
    Vad = ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD_SHORT), 'SdaV');

    Status = MiFindEmptyAddressRange (CapturedRegionSize,
                                                  Alignment,
                                                  (ULONG)ZeroBits,
                                                  &StartingAddress);

    Status = MiInsertVadCharges (Vad, Process);                                              
}

这里创建一个内存管理节点,然后保存内存信息,并插入到进程的用户内存空间中。

4.2 MiProtectVirtualMemory

上面的VirtualAlloc分配了一个内存区,插入到了进程的用户内存表中,但是,我们知道可以使用VirtualProtect来改变其中某些页面的属性。

因此对于一个MMVAD还会形成一系列的Region,每个Region的属性是不同的,所有的Region组成了MMVAD,修改属性的函数为MiProtectVirtualMemory,在Reactos中,这个实现过程如下:

Windows 虚拟内存管理_第1张图片

至于MmSplitRegion这个函数,是修改其中某块的属性,然后针对属性进行修改,如果修改过后的属性和其他相邻的Region相同,则形成合并(这里是Reactos的代码,但是原理上面来说,应该都差不多)。

5. 应用

5.1 模块枚举

从上面的分析,我们知道可以直接使用如下函数枚举进程的模块

NTSTATUS ZwQueryVirtualMemory(
  _In_      HANDLE                   ProcessHandle,
  _In_opt_  PVOID                    BaseAddress,
  _In_      MEMORY_INFORMATION_CLASS MemoryInformationClass,
  _Out_     PVOID                    MemoryInformation,
  _In_      SIZE_T                   MemoryInformationLength,
  _Out_opt_ PSIZE_T                  ReturnLength
);

typedef enum _MEMORY_INFORMATION_CLASS {
    MemoryBasicInformation
#if DEVL
    ,MemoryWorkingSetInformation
#endif
    ,MemoryMappedFilenameInformation
    ,MemoryRegionInformation
    ,MemoryWorkingSetExInformation
} MEMORY_INFORMATION_CLASS;

更加底层的,我们可以直接枚举进程空间中的内存获取模块信息。

5.2 隐藏进程模块

既然我们可以通过VadRoot获取进程模块,那么通过同样的方法,我们也可以通过这里进行隐藏,具体实现可以参考网上的方法,但是这种方法不是特别稳定应当慎重使用。

5.3 内存隐藏

很容易,如果我们将MMVDA中的内存长度进行修改,就会导致内存隐藏,具体实现参考网上的一些实现方法,但是这种方法也不是稳定的方案,应当慎重使用。

你可能感兴趣的:(Windows系统原理)