ReactOS 图形系统分析(23):引擎内存管理 — mem.c

ReactOS 图形系统分析(23):引擎内存管理 --- mem.c

1. 概述

mem.c 位于 win32ss/gdi/eng/mem.c(文件头自述 "GDI Driver Memory Management Functions"),实现 GDI 引擎的内存管理服务族 ------既是引擎内部各模块的通用分配入口,也是 DDI 契约中供显示/字体/打印驱动调用的内存服务(win32k.sys 导出)。

按能力分三类:

复制代码
┌─────────────────────────────────────────────────────────────┐
│ gdi/eng/mem.c(本分析对象)                                   │
│   A. 内核池分配                                              │
│      EngAllocMem(Paged/NonPaged + 清零)/ EngFreeMem        │
│   B. 用户虚拟内存分配                                        │
│      EngAllocUserMem / EngFreeUserMem(ZwAllocateVirtMem)   │
│   C. 用户内存安全锁定(防内核使用中被用户释放)                │
│      EngSecureMem / EngSecureMemForRead / EngUnsecureMem     │
│      + HackSecureVirtualMemory / HackUnsecureVirtualMemory   │
│        (MDL 探测+锁定,供内核访问用户缓冲区)                │
├─────────────────────────────────────────────────────────────┤
│ 底层:ExAllocatePoolWithTag / Zw(Alloc|Free)VirtualMemory /  │
│       MmSecureVirtualMemory / IoAllocateMdl+MmProbeAndLock   │
└─────────────────────────────────────────────────────────────┘

重要观察: EngAllocMem 是全引擎最通用的分配服务------全树 32 个文件、65 处调用(eng/ntgdi/字体驱动/显示驱动/DirectX 全在用),是《分析_4》2.x 各对象(SURFACE/EBRUSHOBJ/EXLATEOBJ 等)与《分析_18/19》等文件分配内存的共同底层。


2. 内核池分配 --- EngAllocMem / EngFreeMem

c 复制代码
PVOID EngAllocMem(ULONG fl, ULONG cjMemSize, ULONG ulTag)
{
    pvBaseAddress = ExAllocatePoolWithTag((fl & FL_NONPAGED_MEMORY) ?
                                                  NonPagedPool : PagedPool,
                                          cjMemSize, ulTag);
    if (pvBaseAddress == NULL) return NULL;
    if (fl & FL_ZERO_MEMORY) RtlZeroMemory(pvBaseAddress, cjMemSize);
    return pvBaseAddress;
}

VOID EngFreeMem(PVOID pvBaseAddress)
{
    if (pvBaseAddress)                       // Windows 允许传 NULL
        ExFreePoolWithTag(pvBaseAddress, 0); // tag 0 == ExFreePool(不校验 tag)
}

标志(winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h#L1358-L1359)):

标志 含义
FL_ZERO_MEMORY (0x1) 分配后清零(RtlZeroMemory)
FL_NONPAGED_MEMORY (0x2) NonPagedPool 分配(缺省为 PagedPool)

设计要点:

  • 默认分页池 (大部分 GDI 对象可分页换出);需要锁路径/中断上下文时显式 FL_NONPAGED_MEMORY(如信号量 ERESOURCE,见《分析_20》)。
  • ulTag 用于池标签调试(WinDbg !pool 可看归属);EngFreeMem 用 tag 0 等价 ExFreePool,不校验标签。
  • EngAllocSectionMem(《分析_21》会话空间大块共享分配)形成"小对象用池 / 大共享用节"的分层。

3. 用户虚拟内存分配 --- EngAllocUserMem / EngFreeUserMem

c 复制代码
PVOID EngAllocUserMem(SIZE_T cjMemSize, ULONG ulTag)
{
    Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &pvBaseAddress, 0,
                                     &cjMemSize, MEM_COMMIT | MEM_RESERVE,
                                     PAGE_READWRITE);
    ...
    /* TODO: Add allocation info to AVL tree (stored inside W32PROCESS) */
}

VOID EngFreeUserMem(PVOID pvBaseAddress)
{
    ZwFreeVirtualMemory(NtCurrentProcess(), &pvBaseAddress, &cjSize, MEM_RELEASE);
}

语义:当前进程的用户地址空间 一次性"保留+提交"一段读写内存(用于内核替用户分配 GDI 位图缓冲等)。树内消费者:

消费者 用途
surface.c(file:///d:/reactos/win32ss/gdi/eng/surface.c#L238) BMF_USERMEM 位图的像素内存(CreateBitmap 用户内存模式)
dibobj.c(file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L2017) 位图信息查询/转换的临时用户缓冲(bm.bmBits

已知 TODO(源码标注): 分配信息应记入 W32PROCESS 的 AVL 树以便进程退出时清理,当前未实现(若进程中途退出,该用户内存可能泄漏/由 VAD 回收)。


4. 用户内存安全锁定 --- EngSecureMem / EngSecureMemForRead / EngUnsecureMem

Windows 语义: 当内核正在使用一块用户模式内存 (如 DIB Section 位图、系统调用传入的缓冲)时,必须"锁定"它------否则用户线程可能并发释放该内存(VirtualFree/退出进程),内核继续写就崩溃。EngSecureMem 返回不透明句柄,用毕 EngUnsecureMem 解锁。

ReactOS 现状(关键): 三个函数的真实现被 {// HACK!!! 块短路,真正的 MmSecureVirtualMemory 分支不可达

c 复制代码
HANDLE EngSecureMem(PVOID Address, ULONG Length)
{
    {// HACK!!!
        _SEH2_TRY { ProbeForWrite(Address, Length, 1); }   // 仅做可写性探测
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return NULL); }
        _SEH2_END;
        return (HANDLE)-1;          // ← 恒返回 -1,假句柄!
    }
    return MmSecureVirtualMemory(Address, Length, PAGE_READWRITE);  // 不可达
}

VOID EngUnsecureMem(HANDLE Mem)
{
    if (Mem == (HANDLE)-1) return;  // 假句柄直接跳过
    MmUnsecureVirtualMemory((PVOID)Mem);
}
  • EngSecureMem 只做 ProbeForWrite 校验后返回假句柄 -1(无真正锁定);EngSecureMemForRead 类似(逐页读探测)。
  • EngUnsecureMem-1 直接返回------成对调用下行为等价"只探测不锁定"
  • 这意味着 ReactOS 当前没有真正防止用户释放正在使用内存的机制(属安全缺口/简化)。

消费者: dibobj.c(file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L1166)(DIB 位图锁定)、mapping.c(file:///d:/reactos/win32ss/gdi/eng/mapping.c#L58)(EngMapSectionView 后锁定视图,见《分析_21》)。


5. MDL 锁定 --- HackSecureVirtualMemory / HackUnsecureVirtualMemory

供内核访问用户缓冲区(如 DIB 引擎读写用户位图)的 MDL 探测+映射辅助:

c 复制代码
PVOID HackSecureVirtualMemory(PVOID Address, SIZE_T Size,
                              ULONG ProbeMode, PVOID *SafeAddress)
{
    pmdl = IoAllocateMdl(Address, Size, FALSE, TRUE, NULL);
    MmProbeAndLockPages(pmdl, UserMode, Operation);   // 探测+锁定用户页
    *SafeAddress = MmGetSystemAddressForMdlSafe(pmdl, NormalPagePriority);
    return pmdl;              // 返回 MDL 作为"句柄"
}

VOID HackUnsecureVirtualMemory(PVOID SecureHandle)
{
    MmUnlockPages(pmdl);      // 解锁
    IoFreeMdl(pmdl);          // 释放 MDL
}
  • 流程:IoAllocateMdlMmProbeAndLockPages(SEH 保护,探测失败返回 NULL)→ MmGetSystemAddressForMdlSafe 得到系统空间映射地址,内核即可直接读写用户缓冲;
  • 是 4.3 节"只探测不锁定"的安全替代方案(真正的页锁定 + 系统地址映射),声明于 ntgdi/misc.h(file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L29-L30)。

6. 导出与消费路径

6.1 win32k.spec 导出(win32k.spec(file:///d:/reactos/win32ss/win32k.spec))

复制代码
@ stdcall EngAllocMem(long long long)          # L9
@ stdcall EngAllocPrivateUserMem(ptr ptr long) # L10(本文件未实现,spec 保留)
@ stdcall EngAllocUserMem(ptr long)            # L12
@ stdcall EngFreeMem(ptr)                      # L57
@ stdcall EngFreePrivateUserMem(ptr ptr)       # L59(本文件未实现)
@ stdcall EngFreeUserMem(ptr)                  # L61
@ stdcall EngSecureMem(ptr long)               # L118
@ stdcall EngUnsecureMem(ptr)                  # L140

6.2 其他导出路径

路径 内容
dxgthk.spec(file:///d:/reactos/win32ss/reactx/dxgthk/dxgthk.spec#L3-L14) 重新导出 EngAllocUserMem/EngFreeUserMem(用户态 DirectX 驱动用)
win32u/umpdstubs(file:///d:/reactos/win32ss/gdi/eng/umpdstubs.c#L9) NtGdiUMPDEngFreeUserMem(UMPD 用户内存释放)

6.3 调用链示例

#mermaid-svg-uhc0aKtKrEtMDVuE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uhc0aKtKrEtMDVuE .error-icon{fill:#552222;}#mermaid-svg-uhc0aKtKrEtMDVuE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uhc0aKtKrEtMDVuE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uhc0aKtKrEtMDVuE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uhc0aKtKrEtMDVuE .marker.cross{stroke:#333333;}#mermaid-svg-uhc0aKtKrEtMDVuE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uhc0aKtKrEtMDVuE p{margin:0;}#mermaid-svg-uhc0aKtKrEtMDVuE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster-label text{fill:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster-label span{color:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster-label span p{background-color:transparent;}#mermaid-svg-uhc0aKtKrEtMDVuE .label text,#mermaid-svg-uhc0aKtKrEtMDVuE span{fill:#333;color:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE .node rect,#mermaid-svg-uhc0aKtKrEtMDVuE .node circle,#mermaid-svg-uhc0aKtKrEtMDVuE .node ellipse,#mermaid-svg-uhc0aKtKrEtMDVuE .node polygon,#mermaid-svg-uhc0aKtKrEtMDVuE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uhc0aKtKrEtMDVuE .rough-node .label text,#mermaid-svg-uhc0aKtKrEtMDVuE .node .label text,#mermaid-svg-uhc0aKtKrEtMDVuE .image-shape .label,#mermaid-svg-uhc0aKtKrEtMDVuE .icon-shape .label{text-anchor:middle;}#mermaid-svg-uhc0aKtKrEtMDVuE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uhc0aKtKrEtMDVuE .rough-node .label,#mermaid-svg-uhc0aKtKrEtMDVuE .node .label,#mermaid-svg-uhc0aKtKrEtMDVuE .image-shape .label,#mermaid-svg-uhc0aKtKrEtMDVuE .icon-shape .label{text-align:center;}#mermaid-svg-uhc0aKtKrEtMDVuE .node.clickable{cursor:pointer;}#mermaid-svg-uhc0aKtKrEtMDVuE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uhc0aKtKrEtMDVuE .arrowheadPath{fill:#333333;}#mermaid-svg-uhc0aKtKrEtMDVuE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uhc0aKtKrEtMDVuE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uhc0aKtKrEtMDVuE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uhc0aKtKrEtMDVuE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uhc0aKtKrEtMDVuE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uhc0aKtKrEtMDVuE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster text{fill:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE .cluster span{color:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uhc0aKtKrEtMDVuE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uhc0aKtKrEtMDVuE rect.text{fill:none;stroke-width:0;}#mermaid-svg-uhc0aKtKrEtMDVuE .icon-shape,#mermaid-svg-uhc0aKtKrEtMDVuE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uhc0aKtKrEtMDVuE .icon-shape p,#mermaid-svg-uhc0aKtKrEtMDVuE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uhc0aKtKrEtMDVuE .icon-shape .label rect,#mermaid-svg-uhc0aKtKrEtMDVuE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uhc0aKtKrEtMDVuE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uhc0aKtKrEtMDVuE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uhc0aKtKrEtMDVuE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} EngAllocMem(65 处调用:eng/ntgdi/驱动)
ExAllocatePoolWithTag

Paged/NonPaged
EngAllocUserMem
ZwAllocateVirtualMemory

用户地址空间
EngSecureMem
ProbeForWrite + 假句柄-1

(真实锁定未启用)
HackSecureVirtualMemory
MDL 探测锁定

  • 系统地址映射

7. 与《分析_4》标注对照

标注 核对
《_4》第 43 行:mem.c =「引擎内存分配」 准确。补充:三类能力(内核池/用户内存/安全锁定);EngAllocMem 为全引擎通用分配入口;EngSecureMem 当前为"只探测不锁定"的 HACK 简化

8. 源码索引

文件 关键内容
mem.c(file:///d:/reactos/win32ss/gdi/eng/mem.c) EngAllocMem/FreeMem、EngAllocUserMem/FreeUserMem、EngSecureMem(ForRead)/UnsecureMem、HackSecure/UnsecureVirtualMemory
winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h#L1358-L1359) FL_ZERO_MEMORY / FL_NONPAGED_MEMORY
misc.h(file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L29-L30) Hack* 声明
surface.c(file:///d:/reactos/win32ss/gdi/eng/surface.c#L238) BMF_USERMEM 位图用 EngAllocUserMem
mapping.c(file:///d:/reactos/win32ss/gdi/eng/mapping.c#L58) EngMapSectionView 用 EngSecureMem(《分析_21》)
dibobj.c(file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L1166) DIB 位图锁定 / 用户缓冲分配

关联模块:

  • 大块共享分配:EngAllocSectionMem(《分析_21》mapping.c,会话空间)
  • 分配对象:SURFACE/EBRUSHOBJ/EXLATEOBJ/PATHOBJ 等(《分析_4/17/18/19》)
  • 内存标签:GDITAG_* 系列(ExAllocatePoolWithTag)

9. 各函数使用说明

9.1 EngAllocMem --- 分配内核池内存(最常用)

c 复制代码
PVOID EngAllocMem(ULONG fl, ULONG cjMemSize, ULONG ulTag);
参数 说明
fl 标志:FL_ZERO_MEMORY(清零)/ FL_NONPAGED_MEMORY(非分页池);可 OR 组合
cjMemSize 请求字节数
ulTag 4 字节池标签(如 'rusG'),用于调试 !pool 归类

使用要点:

  1. 默认是分页池 ------只有锁路径/中断/DMA 等必须常驻的场景才加 FL_NONPAGED_MEMORY(例:信号量 ERESOURCE 见《分析_20》)。
  2. 返回 NULL 表示分配失败,必须判空再使用。
  3. 要求清零内存时传 FL_ZERO_MEMORY,省去手动 memset
  4. 释放必须用 EngFreeMem(配 tag 0),不要直接用 ExFreePool(会丢失"允许 NULL"的语义约定)。

典型用法:

c 复制代码
PBRUSH pbr = EngAllocMem(FL_ZERO_MEMORY, sizeof(BRUSH), GDITAG_BRUSH);
if (!pbr) return NULL;               // 失败处理
...
EngFreeMem(pbr);                     // 释放(NULL 安全)

9.2 EngFreeMem --- 释放 EngAllocMem 分配的内存

c 复制代码
VOID EngFreeMem(PVOID pvBaseAddress);

使用要点:

  1. 允许传 NULL(与 Windows 一致),无需外层判空。
  2. 内部用 tag 0 调用 ExFreePoolWithTag,等价 ExFreePool,不校验标签。
  3. 只能释放 EngAllocMem(或驱动直接用 ExAllocatePoolWithTag)分配的内核池内存;不能释放用户内存或节映射内存。

9.3 EngAllocUserMem --- 在当前进程用户空间分配内存

c 复制代码
PVOID EngAllocUserMem(SIZE_T cjMemSize, ULONG ulTag);

使用要点:

  1. 分配在当前进程的用户地址空间ZwAllocateVirtualMemoryMEM_COMMIT|MEM_RESERVE,PAGE_READWRITE),内核可直接写,用户态可读。
  2. 典型用途:为 BMF_USERMEM 位图提供像素缓冲、为位图信息查询提供临时缓冲。
  3. 释放必须用 EngFreeUserMem
  4. 已知局限(源码 TODO):分配记录未纳入 W32PROCESS 的 AVL 树------若分配后进程异常退出,该内存靠进程 VAD 回收,引擎不主动跟踪。

典型用法:

c 复制代码
pvBits = EngAllocUserMem(cjBits, 0);
if (!pvBits) return FALSE;
...                              /* 内核填像素 */
EngFreeUserMem(pvBits);          /* 用毕释放 */

9.4 EngFreeUserMem --- 释放用户空间内存

c 复制代码
VOID EngFreeUserMem(PVOID pvBaseAddress);

使用要点:

  1. 对应 EngAllocUserMem 的逆操作(ZwFreeVirtualMemoryMEM_RELEASE)。
  2. 注意:EngFreeUserMem 不判 NULL (与 EngFreeMem 不同)------调用前需自行确认指针有效。
  3. 只释放"由内核替当前进程分配"的用户内存,不要用来释放应用自己 VirtualAlloc 的内存。

9.5 EngSecureMem --- 锁定用户内存(当前为 HACK 简化版)

c 复制代码
HANDLE EngSecureMem(PVOID Address, ULONG Length);

使用要点:

  1. 语义:防止内核使用期间用户并发释放该内存;返回不透明句柄,用毕 EngUnsecureMem 解锁。
  2. ReactOS 现状 :只做 ProbeForWrite 可写性探测,恒返回假句柄 (HANDLE)-1;真正的 MmSecureVirtualMemory{// HACK!!! 块短路,没有真正锁定
  3. 因此安全校验逻辑要自己写 :若后续代码依赖"内存已被锁定",当前实现并不保证------需自行加锁或改用 HackSecureVirtualMemory
  4. 返回值判 NULL/-1 均表示失败(探测不通过)。

典型用法:

c 复制代码
hSecure = EngSecureMem(pjBits, cjMaxBits);
if (!hSecure) return FALSE;      /* 探测失败 */
...                              /* 使用 pjBits */
EngUnsecureMem(hSecure);         /* 假句柄 -1 会被直接跳过 */

9.6 EngSecureMemForRead --- 锁定只读用户内存

c 复制代码
HANDLE EngSecureMemForRead(PVOID Address, ULONG Length);

使用要点:

  1. EngSecureMem 同构,但做的是逐页读探测ProbeForRead + 每页读一次),语义为"内核将只读该缓冲"。
  2. 同样返回假句柄 -1,真实锁定未启用(HACK)。
  3. 使用场景:只读用户缓冲(如 GetBitmapBits 的输入参数校验)。

9.7 EngUnsecureMem --- 解除锁定

c 复制代码
VOID EngUnsecureMem(HANDLE Mem);

使用要点:

  1. EngSecureMem/EngSecureMemForRead 成对使用。
  2. 遇假句柄 (HANDLE)-1 直接返回(HACK 配套逻辑);真实句柄才调用 MmUnsecureVirtualMemory
  3. 不要对 NULL 或未加锁的地址调用(无防护),成对性由调用方保证。

9.8 HackSecureVirtualMemory --- 用 MDL 锁定用户缓冲并映射到系统空间

c 复制代码
PVOID HackSecureVirtualMemory(PVOID Address, SIZE_T Size,
                              ULONG ProbeMode, PVOID *SafeAddress);
参数 说明
Address / Size 用户缓冲地址与大小
ProbeMode PAGE_READONLY(读锁)/ PAGE_READWRITE(读写锁)
SafeAddress 输出:可被内核直接访问的系统空间映射地址

使用要点:

  1. 返回值为 PMDL (作为后续释放的句柄),不是映射地址------映射地址在 SafeAddress 输出。
  2. 流程:IoAllocateMdlMmProbeAndLockPages(SEH 保护,失败返回 NULL)→ MmGetSystemAddressForMdlSafe
  3. 用毕必须调用 HackUnsecureVirtualMemory 释放(解锁 + 释放 MDL)。
  4. 这是当前引擎里真正能锁定用户页的方案(优于 9.5 的探测式 HACK),适合 DIB 引擎需要长时间读写用户位图的场景。

典型用法:

c 复制代码
pmdl = HackSecureVirtualMemory(pvUserBits, cbSize, PAGE_READWRITE, &pvSafe);
if (!pmdl) return FALSE;
memcpy(pvSafe, ...);                 /* 内核通过系统地址访问 */
HackUnsecureVirtualMemory(pmdl);

9.9 HackUnsecureVirtualMemory --- 释放 MDL 锁定

c 复制代码
VOID HackUnsecureVirtualMemory(PVOID SecureHandle);

使用要点:

  1. 参数是 HackSecureVirtualMemory 返回的 PMDL。
  2. 依次 MmUnlockPages + IoFreeMdl,成对释放。
  3. 必须与 9.8 一一对应,过早释放会导致内核访问已解锁页面的错误。

本文档基于 ReactOS 源代码 win32ss/gdi/eng/mem.c 及关联模块分析(2026 年 8 月)

相关推荐
caimouse44 分钟前
ReactOS 图形系统分析(15):窗口对象 — EWNDOBJ(engwindow.c)
c语言·开发语言
青 春 记 忆1 小时前
零基础入门python07:让程序记住数据——JSON文件和异常处理
开发语言·windows·python·json·python3.11
W_326001 小时前
Python 常用标准 / 第三方库:random、tqdm、turtle、jieba 用法
开发语言·python
caimouse2 小时前
ReactOS 图形系统分析(17):区域填充 — EngPaint / IntEngPaint(paint.c)
c语言·开发语言·算法
北风toto2 小时前
研发效能与后端核心技术全景指南:从CI/CD到共性组件实战
java·开发语言·ci/cd
Billy121382 小时前
Day12-C++20 Coroutines(上):协程原理与Promise/Awaitable机制
开发语言·c++进阶学习
01二进制代码漫游日记2 小时前
C++基础入门速通
java·开发语言·c++
Herbert_hwt2 小时前
第六章 Java深入理解接口、函数式接口与lambda表达式
java·开发语言·算法
橙橙笔记2 小时前
C++的学习第三部分
开发语言·c++·学习