尧图精选

Madeira 如何绕开 Jetsam 内存上限:VM_LEDGER_FLAG_NO_FOOTPRINT 完整解析

🕒 发布时间:2026/10/2 18:34:38 📁 来源:尧图网络
Madeira 如何绕开 Jetsam 内存上限VM_LEDGER_FLAG_NO_FOOTPRINT 完整解析【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira在 iPhone 上跑 Windows PC 游戏听起来像天方夜谭但 Madeira 正在做这件事它用 FEX-Emu Wine DXMT 把 x86-64 的 PC 游戏装进一个普通 iOS App。而其中最凶险的一关是 iOS 的Jetsam 内存墙——一旦物理内存超限系统直接杀进程。本项目通过私有 Mach APIVM_LEDGER_FLAG_NO_FOOTPRINT给大块 JIT 内存隐身让它不计入内存上限。本文带你完整看懂这套机制的原理、代码位置和实战结果。一、为什么 Jetsam 是最大拦路虎 iOS 对每个 App 的物理内存phys_footprint有硬性上限iPhone 上通常在 4GB 左右。超了EXC_RESOURCE进程无声无息被杀连崩溃日志都可能来不及写。问题在于要在 iPhone 上翻译执行 x86-64 代码必须有一块大块 JIT 内存池。源码注释里记录的真实数据触目惊心生产环境的 JIT 池默认896MBStikJITHelper.swift 的allocatePool负责分配这块池子生来就是脏页dirty from birth意味着它全额计入内存上限一次实验把池子加到 1024MB结果 Jetsam 在 2.5 分钟时无声击杀了整个 App如果这块池子能不计入上限游戏可用内存立刻多出近 1GB——这就是VM_LEDGER_FLAG_NO_FOOTPRINT的价值。二、核心机制三步隐身流程全部实现集中在 JITAllocator.c 中接口声明见 JITAllocator.h。流程分三步第 1 步创建一个命名内存条目kr mach_make_memory_entry_64( task, entry_size, 0, MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, mem_entry, MACH_PORT_NULL);通过私有 APImach_make_memory_entry_64向内核申请一个命名内存对象。注意这里的关键细节必须用MAP_MEM_NAMED_CREATE它代表整个 VM 对象本身——这一点在后文会再次救场。第 2 步打上 NO_FOOTPRINT 标记kr mach_memory_entry_ownership( mem_entry, TASK_NULL, // 无属主 系统持有 VM_LEDGER_TAG_DEFAULT, VM_LEDGER_FLAG_NO_FOOTPRINT); // 1 0VM_LEDGER_FLAG_NO_FOOTPRINT是一个值为1 0的私有标记定义在 JITAllocator.c 第 40-48 行附近。调用mach_memory_entry_ownership后这块内存的所有常驻页就不再计入 App 的 phys_footprintJetsam 看不见它。这个技巧最早由越狱生态的 MeloNX 项目验证Madeira 将其用于 JIT 场景。第 3 步双重映射一份物理内存两个视图命名内存条目同一块物理内存 / \ RW 视图写生成代码 RX 视图执行生成代码最后用两次vm_map把同一个内存条目分别映射为可读写和可执行两个虚拟地址区间见 JITAllocator.c 第 150-206 行。JIT 编译器往 RW 视图写翻译出来的 ARM64 指令然后从 RX 视图执行——天然满足 W^X写与执行互斥安全要求同时物理内存只占一份。失败处理也很诚实ownership调用失败是非致命的日志会记下错误内存只是照常计入上限App 继续跑JITAllocator.c 第 143-148 行。三、进阶玩法给已分配的 896MB 大池补办豁免上面三步对新建的 JIT 区域很有效。但生产环境的主池不走这条路——它的 RX 页来自调试器iOS 26 的 BRK #0xf00d 协议见 StikJITHelper.swift事后才vm_remap出 RW 别名。所以项目又写了 JITAllocator.h 第 25-29 行声明的jit_make_region_no_footprint()给一块已经映射好的内存补办豁免。它的实现堪称贝塔系统逆向教科书JITAllocator.c 第 227-313 行调用前先用task_info采样phys_footprint由于是 Beta 系统上的无文档私有 API不押注单一方案而是遍历 4 种 (entry 标志, 属主) 组合plain/TASK_NULL、plain/self、share/self、share/TASK_NULL每一步都打印内核返回码kr成功的组合会打印前后 phys_footprint 差值全程非致命最坏情况只是和以前一样被 Jetsam 全额计费用差值而非调用成功与否来证明豁免生效是这段代码最值得学习的设计差值 ≈ 池子常驻字节数说明真省了差值 ≈ 0说明内核嘴上答应了、账还在记。四、结局内核的一次婉拒和一次诚实的妥协 真实战果记录在 ContentView.swift 大段池子大小演进注释中约第 2357-2421 行版本池子大小结果ml3641152MB因 DLL 携带 DWARF 调试段而膨胀ml367896MBstrip 调试段后回归省回 256MBml4211024MB峰值 3837MB压线ml4221024MBJetsam 无声击杀峰值 3904MBml458896MB冻结豁免尝试宣告失败最终结局写得很坦诚4 种组合全部返回kr4KERN_INVALID_ARGUMENT——内核要求一个指向整个 VM 对象的命名条目而 StikDebug 创建的内存对象我们永远无法整体命名所以整池豁免这条路在 iOS 26 上走不通。但机制本身留下了三样东西✅ 双映射 JIT 区域仍可正常享受豁免✅ 一套采样—尝试—验证差值的私有 API 调试范式✅ 896MB 的保守上限配合池子耗尽时的优雅降级让池子耗尽不再等于被 Jetsam 杀进程。五、新手速查相关代码地图 想看的去哪里私有 API 声明与 NO_FOOTPRINT 定义JITAllocator.c双映射区域创建/销毁/写码JITAllocator.cjit_region_create等已映射区域补办豁免JITAllocator.hjit_make_region_no_footprint生产 896MB 池分配与 BRK 协议StikJITHelper.swiftallocatePool池子大小演进史与 Jetsam 事故复盘ContentView.swiftpoolSizeMB注释块构建流程docs/BUILDING.md一句话总结VM_LEDGER_FLAG_NO_FOOTPRINT让内存对 Jetsam 隐身Madeira 把它接进 JIT 分配器并建立了用 phys_footprint 差值验证的严谨流程虽然整池豁免最终被内核拒绝但这次失败换来的是一个更稳的 896MB 上限和一套可复用的私有 API 逆向方法论。【免费下载链接】MadeiraRun x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →