技术笔记 · 2026-08-13

AutoHotkey64.exe 内部:运行时扫描、进程内 Eval 与远程内省

一个 AutoHotkey v2 脚本如何读取正在运行它的解释器、重建嵌入的 x64 机器码、在进程内求值、从内存加载脚本,并对远程 AHK 进程做在线内省。

仓库:MonoEven/ahk-hack-library

目标与方法

目标是把 AutoHotkey64.exe 当作活体解释器分析,并提取足够多的结构来实现表达式求值、内存脚本加载、远程 Hook 和在线函数 Trace。方法是可重复的循环:先读源码找出候选字段,再检查进程内存确认真实偏移,把稳定逻辑编码成位置无关机器码,最后用结构不变量验证每个结果。

本页记录的就是这个循环。

PE 解析与节区映射

每个 x64 AutoHotkey v2 可执行文件都以 MZ DOS 头开始。PE 签名位于 e_lfanew 指向的偏移,可选头使用 PE32+ 魔数 0x20B。节表紧随可选头。

peOff   := NumGet(base, 0x3C, "UInt")
numSec  := NumGet(base + peOff + 6, "UShort")
optSize := NumGet(base + peOff + 20, "UShort")
secTab  := base + peOff + 24 + optSize

loop numSec {
    off := (A_Index - 1) * 40
    name := StrGet(hdr.Ptr + off, 8, "UTF-8")
    vsize := NumGet(hdr + off + 8, "UInt")
    va := NumGet(hdr + off + 12, "UInt")
    rawSize := NumGet(hdr + off + 16, "UInt")
}

扫描器把每个节区保存为 {rva, size, ptr},并按名称分类。三个节区最重要:

节区作用不变量
.text可执行代码函数指针必须落在这里
.rdata只读字符串UTF-16 名字通常在这里
.data可写全局量解释器表在这里

对于加壳构建,节名不可靠。扫描器会退回到基于内容的分类:非 .rsrc 的代码样节区当作可执行节区,可读的非执行节区当作数据/字符串存储。

解释器表发现

解释器在镜像中维护三张表。它们的 x64 记录步长是稳定的:

内容记录步长
g_BIF脚本可见内置函数,如 AbsSin0x20
sMdFunc带类型签名的原生函数,如 MsgBox0x28
g_BIV_AA_ 开头的内置变量0x18

扫描器不硬编码地址。它按指针对齐槽位扫描,只有同时满足以下条件才接受记录:

  • 第一个 qword 解析为可打印 UTF-16 字符串;
  • 第二个 qword 落在 .text
  • 相邻记录按名字排序;
  • 存在 AbsBlockInputAhkPath 等锚点。

最长的合法连续区成为目标表。同一算法以 x64 机器码运行,因此扫描速度与逻辑不依赖 AHK 版本。

源码到内存的验证

源码提供名字与意图,不提供最终偏移。编译器、构建选项和调试器字段都会移动成员。因此流程先用源码形成假设,再用内存确认。

对于 script.h 中的 UserFunc,源码声明了:

class UserFunc : public Func
{
public:
    int mInstances = 0;
    Line *mJumpToLine = nullptr;
    FuncParam *mParam = nullptr;
    Object *mClass = nullptr;
    ...
    VarList mVars {};
    VarList mStaticVars {};
};

内存探针在两个代表性构建上确认了实际 x64 布局:

字段v2.1-alpha.30v2.0.26
mName0x280x20
mJumpToLine0x480x40
mParam0x500x48
mVars0x800x70

偏移不同,所以库在运行时发现它们,不维护版本表。

机器码提取流水线

稳定扫描器写在 lib/mcode/ 下的 C 源码中。构建使用 clang 生成无 CRT、位置无关的 COFF 对象:

clang -c -O2 -target x86_64-pc-windows-msvc -ffreestanding \
  -fno-builtin -fno-stack-protector -fno-unwind-tables \
  -fno-asynchronous-unwind-tables -fno-jump-tables scanner.c

tools/build_mcode.py 执行四步检查:

  • 从 COFF 对象提取 .text
  • 解码每条重定位记录;
  • 只有目标仍在 .text 内的 IMAGE_REL_AMD64_REL32 才被接受;
  • 任何引用逃出代码块的内容都会被拒绝。

最终十六进制字符串嵌入 ahk_hack_single.ahkMCode() 把字符串解码为 Buffer,修改为可执行保护,并返回给 DllCall

EvalNative:表达式求值分析

AutoHotkey 不会直接求值表达式字符串。脚本加载时,Line::ExpressionToPostfix 把表达式编译成 ExprTokenType 后缀数组,Line::ExpandExpression 之后再执行该数组。

EvalNative() 构造解析器本来会创建的临时 LineArgStruct,然后调用这两个函数。因此它复用了解释器自己的语法与变量解析。

内部定位器机器码发现以下符号:

符号来源发现方式
ExpressionToPostfix错误串 "Missing operand."RIP 相对引用 + prologue 回退
ExpandExpression错误串 "Error evaluating expression."RIP 相对引用 + prologue 回退
gScript最频繁的 lea rcx,[rip+disp] 目标频率过滤
FindOrAddVar六参数 prologue签名匹配
SYM_INVALID后缀终止符求值器内立即数

求值器使用的 token 布局:

字段偏移类型
value0x00指针/int64/double 联合
marker/usage0x08指针或 VarRefUsageType
symbol0x1032 位 SymbolType
stride0x18结构总大小
#Include ahk_hack_single.ahk
AhkMagic.Init()

MsgBox AhkMagic.EvalNative("1 + 2 * 3")           ; 7
MsgBox AhkMagic.EvalNative("2 * 3.5")             ; 7.0
MsgBox AhkMagic.EvalNative("Abs(-5)")             ; 5
MsgBox AhkMagic.EvalNative("SubStr(`"abc`", 2)")  ; "bc"

myVar := 42
MsgBox AhkMagic.EvalNative("myVar + 1")           ; 43

EvalScript:解析器与内存加载

表达式求值无法定义函数或类。EvalScript() 通过内存 TextStream 把多行文本喂给解释器的 include 管线。

相关管线:

ResultType Script::LoadIncludedFile(TextStream *fp)
{
    int source_file_index = mCurrFileIndex;
    bool blocks_previously_open = mLineParent || mClassObjectCount;
    auto module_previously_open = mCurrentModule;
    bool caller_backcompatmode = mBackCompatMode;
    ...
}

加载前,库保存并在之后恢复:

  • mOpenBlock / mLineParent
  • mPendingRelatedLine
  • mLastParamInitializer
  • mPendingHotkey
  • mExprFuncIndex
  • mNextLineIsFunctionBody
  • mIgnoreNextBlockBegin
  • mBackCompatMode
  • mClassObjectCount
  • mCurrentModulemLastModule

解析器状态锚点从 LoadIncludedFile prologue 中读取:

  • q:指向 mOpenBlock/mLineParent 的偏移;
  • d:指向 mClassObjectCount 的偏移。

TextStream 同时填充两种可能的 mData 区域,因此同一对象可用于 2.0 与 2.1 布局。

远程内省

AttachRemote(pid) 使用 OpenProcess 打开目标,读取 PE 镜像,扫描解释器表,并返回 hook map。

远程求值注入线程 stub、定位器和求值器 blob:

步骤操作
1VirtualAllocEx 分配 scratch 块
2写入 stub、定位器、求值器、表达式、输出缓冲
3CreateRemoteThread 创建远程线程
4等待并读取结果缓冲
5释放 scratch 块

RemoteEvalScript() 使用目标自己的 LoadIncludedFilePreparseExpressions。注入线程会在 preparse 前设置 g->CurrentFunc,因此 class 方法与 Super 能正确解析。

hook := AhkMagic.AttachRemote(pid)
MsgBox AhkMagic.RemoteEval(hook, "1 + 2 * 3")     ; 7

script := "
(
rhkAdd(a, b) {
    return a + b
}
rhkAdd(1, 2)
)"
MsgBox AhkMagic.RemoteEvalScript(hook, script)    ; 3

函数体替换交换 mJumpToLine

AhkMagic.RemoteEvalScript(hook, "
(
NewA() {
    return 42
}
NewA()
)")
AhkMagic.RemoteReplaceFuncBody(hook, "A", "NewA")
Send("{F9}")

目标开始输出 A()=42,原始输出为 A()=1

动态偏移发现

远程布局扫描器从存活内存推导 mFuncsmFuncsCount、函数名字段与 mJumpToLine

  • 候选 array/count 对通过读取每个 Func 指针验证;
  • mName 是指向合法标识符字符串的指针;
  • mJumpToLine 指向包含回指所属 FuncLine
  • 回引用槽位在普通函数之间保持一致。

结构验证器随后发现:

结构验证字段
Lineaction、argc、arg 指针、attribute、next
ArgStructis_expression、postfix 指针、deref 指针
ExprTokenTypestride、symbol 偏移、usage 偏移、value 偏移
DerefTypemarker、var、type、length

Token 数组通过走到 SYM_INVALID 并在终止符前要求一个 SYM_VAR token 来验证。DerefType 从运行进程中已有的 ArgStruct.deref 数组读取。

TraceFunction:基于克隆的 Trace

函数 Trace 比函数替换更难。最初实现重命名目标函数并注入 wrapper,结果失败,因为:

  • 函数名同时存在于 Func.mName 与全局 Var.mName
  • mFuncsVarList 是排序数组;
  • 只改一边会破坏二分查找;
  • 只替换函数体不会重新绑定参数 Var 对象。

稳定修复来自阅读 UserFunc::Call

bool UserFunc::Call(ResultToken &aResultToken, ExprTokenType *aParam[], int aParamCount)
{
    ...
    for (j = 0; j < mParamCount; ++j)
    {
        FuncParam &this_formal_param = mParam[j];
        ...
    }
}

调用路径通过 mParam[i].var 写入参数。因此两个函数对象可以共享同一组参数 Var,两个函数体都能看到相同值。

最终设计:

  • 克隆 UserFunc 对象;
  • 为克隆体创建 VAR_CONSTANT 全局别名;
  • 注入调用克隆体的 wrapper;
  • 把 wrapper 的参数 Var 别名回原函数参数;
  • 替换原函数体指针;
  • Untrace 时恢复原 mJumpToLine

Wrapper 写入目标端全局字符串,因此 RemoteEval 可以读取 Trace 日志,而不依赖求值线程内的文件 I/O。

验证矩阵

tools/verify_all_runtimes.ps1 对本机每个运行时执行十三项测试:

测试覆盖
ahk_live_cross_smokeattach、快照、清单、替换、Watch
ahk_live_test完整 Trace/替换路径
ahk_live_product_test会话与补丁回滚
ahk_live_six_test观测、故障、桌面、取证、代理、CSV
ahk_live_benchmarkeval/快照/清单耗时
remote_hook_test远程求值、脚本、class、函数替换
evalscript_inproc进程内脚本加载
evalscript_repeat重复函数/class 加载
evalscript_classclass 定义
eval_object_syntax_test进程内对象返回值与语法错误存活
remote_empty_object_test远程对象返回值与语法错误存活
remote_empty_target_test无用户函数目标的探针注入重试
remote_deep_redirect_test直接与动态调用链上的深重定向

当前结果:

运行时家族构建结果
2.0 betabeta.9、beta.10、beta.12、beta.13、beta.15各 13/13
2.0 rcrc.1、rc.3各 13/13
2.0 stable0.0、0.2、0.3、0.4、0.26各 13/13
2.1 alphaalpha.1、alpha.4、alpha.13、alpha.16、alpha.30各 13/13
本机开发副本两个 2.0-beta 二进制各 13/13

总计:19 个运行时,全部 13/13。

11.1 回归覆盖:错误捕获

GUI 会把每个 AhkLiveResult 转成可见的日志行。OnError() 同时把异常字段写入 GUI 日志和 %TEMP%\ahk_live_gui_error.logSafeStr() 序列化 Map/Array 值,避免第二个错误再次弄崩日志器。Watch 与观测错误会作为 Error 对象转发给回调。--shot 会选中指定页签并写就绪标记,供完整窗口截图自动化使用。

AhkLive_OnError(e, exitCode := 0) {
    msg := "ERR " e.What " | " e.Message
        . " | " e.File ":" e.Line
    FileAppend(msg "`n", A_Temp "\ahk_live_gui_error.log")
    return true
}

OnError(AhkLive_OnError)

回归测试 tests\ahk_live_regression.ahk 覆盖构建 GUI 时发现的三类失败:

  • EvalScript 之后执行 TraceFunction,要求缓存 script_layoutscript_loc 回填 find_var
  • 快照表达式在括号内含逗号,不能被当成配置分隔符拆分;
  • 重复的顶层函数名,必须在 LoadIncludedFile 执行前拒绝:
for name in AhkMagic._DeclaredFunctionNames(text) {
    if AhkMagic._RemoteFindUserFuncs(h, layout, name).Length
        throw Error("function name already exists in target: " name, -1)
}

新回归测试已在 2.1-alpha.302.0.26 通过。19 运行时十三项矩阵(现已包含上述四个鲁棒性回归)仍然是完整兼容门槛,19 个运行时全部 13/13。

安全与边界

这个库会分配可执行内存、执行机器码,并写入当前或目标进程的解释器内存。它不具备沙箱隔离。

规则:

  • 只附加自己拥有的进程;
  • 反复实验使用一次性目标;
  • 补丁用 finallyAhkLivePatchSession.Rollback() 恢复;
  • 永远不要 eval 不可信输入;
  • 定位失败应视为错误,不猜测偏移。

加壳或恶意构建可能改节名或删字符串。内容分类扫描可以处理常见加壳器,但恶意构建仍可能绕过发现。

失败行为

每一条读取解释器内存或打补丁的路径都设计为显式失败:

领域行为
表与导出扫描记录数按扫描器契约封顶,每个名字指针使用前做内存映射检查;不支持的布局抛错而不是越界读取
语法错误EvalScript/RemoteEvalScript 用解释器自己的 /validate 预检文本(远程用目标自己的可执行文件);验证器静默运行并自行捕获输出(不弹窗),超时即杀进程树
远程超时挂起的求值 10 秒后抛错并泄漏注入块,因为释放目标仍在执行的内存会损坏目标
对象结果EvalNative 返回活的 Func/Class 对象;RemoteEval 返回目标侧对象地址
空目标目标没有用户函数时,RemoteEvalScript 注入随机命名探针函数并重试布局发现
补丁PatchBifObjectRemoteDeepRedirect 原子提交并支持回滚;发现探针在 finally 中恢复解释器快照

继续阅读

附录:单文件核心源码

以下副本由构建脚本根据仓库内文件生成。

ahk_hack_single.ahk
; ahk-hack standalone core.
;
; One-file AutoHotkey v2 scanner for AutoHotkey executables.  Copy this file
; anywhere and #Include it, or run ahk_hack_demo.ahk for a self-test.
; The x64 blob below is generated from lib/mcode/scanner.c by
; tools/build_mcode.py; it locates g_BIF, sMdFunc and g_BIV_A inside the
; running interpreter and copies entry metadata into a caller buffer.
MC_REMOTE_CALL_STUB_X64 := "564883ec304889ce0f1041284c8b49204c8b4118488b4908488b56100f11442420ff1689463831c04883c4305ec3"
MC_REMOTE_EVAL_STUB_X64 := "564881ec800000004889ce0f1041304c8b49284c8b4120488b4910488b56180f11442420ff1689c1894678b80100000085c9755b488b4638488b4e400f10000f104810488b40204c8b96800000000f1056604c8b4e584c8b4650488b56484c8954247048894424680f114c24580f114424480f57c00f114424380f11542420c744243001000000ff560889467c31c04881c4800000005ec3"
MC_PE_EXPORT_SCANNER_X64 := "415741564155415456575553504885c90f94c04885d2410f94c04108c0b8010000000f85f00000006681394d5a0f85e5000000448b413c41813c08504500000f85d30000006641817c08180b020f85c5000000418bb40888000000458b8c088c0000004885f6410f94c24585c9410f94c34508d34180fb0175190f57c00f1102be100000004531c94c890c3231c0e985000000458b4408504d85c0747b4439c673764589c94d89ca4901f24d39c2410f97c24183f928410f92c14508d17559448b740e18448b540e144d85d2410f94c14585f6410f95c34584d9753c4181fe0010000041b900100000450f42ce4c8d3c0e458b5f204b8d3c8b4c39c7771a8b7c0e244a8d1c4f4c39c3770d8b5c0e1c4e8d24934d39c476114883c4085b5d5f5e415c415d415e415fc38b740e10897424044c893a48c7421000000000be080000004585f60f843bffffff4801cb4901cb4801cf4e8d34cd000000004f8d34764531ff458b234539c473ae440fb72f4539ea76a5428b2cab4439c5739c4901cc4e89643a184a896c3a2044036c240446896c3a2842c7443a2c000000004983c3044883c7024983c7184d39fe75b5e9d6feffff"
MC_BIF_SCANNER_X64 := "4157415641554154565755534881ec780200004885c90f94c04885d2410f94c04108c0be010000000f85060700006681394d5a0f85fb0600008b413c813c08504500000f85eb06000066817c08180b020f85de060000440fb7540806664183fa1041b910000000450f42ca448b5c08504d8d040b4d85db4d0f44c3664585d20f8400030000440fb7540814410fb7f94801c84c01d0448d0cfd000000004f8d0c894c8d5424704531dbbb010000004531f631edeb4c660f7f442450440fb67c2452440fb66424534180f7614180f4744508fc440fb67c24544180f761400fb6ed4508e70f44eb662e0f1f8400000000004983c3284983c2204d39d90f84ed000000468b7c18204585ff460f447c1828468b6418244901cc4d01e74d39c74d89c54d0f42ef4d85c04d0f44ef4d89224d896a084e8b7c181866490f6ec7660f7f442460440fb66424604d897a134180fc2e759e66410f7ec741c1ef084180ff640f8450ffffff450fb6ff4183ff7274414183ff740f8577ffffff660f7f442430440fb67c2432440fb66424334180f7654180f4784508fc440fb67c24344180f774450fb6f64508e7440f44f3e940ffffff660f7f442440807c2442640f852fffffff807c2443610f8524ffffff807c2444740f8519ffffff66440fc5f80241c1ef084180ff61400fb6ede9f5feffff4584f60f95c14084ed0f95c020c889f9c1e1054531c0eb2d662e0f1f8400000000006642c784048000000000004531c946888c04820000004983c0204c39c10f8454010000460fb69404830000004180fa2e753e4280bc04840000007275434280bc04850000007375384280bc048600000072752d4280bc048700000063410f94c184c0746ceb21662e0f1f84000000000084c0758c4531c9eb570f1f80000000004531c984c074494180fa2e0f856fffffff4280bc048400000074754d4280bc04850000006575424280bc04860000007875374280bc048700000074410f94c1eb2b6666666666662e0f1f8400000000004180f10146888c048000000046888c0481000000e925ffffff4531c946888c04800000004280bc04840000007275304280bc04850000006475254280bc048600000061751a4280bc048700000074750f4280bc048800000061410f94c1eb034531c946888c04810000004280bc04840000006475284280bc048500000061751d4280bc04860000007475124280bc048700000061410f94c1e9a1feffff4531c9e999feffff31ff488d420848894424284989d74889542420488d4c247041b92000000089fa4531c0e8a303000085c00f847f0300004d8d7720498d47184c897424284889442420488d4c2470be0100000041b92800000089fa41b801000000e86c03000085c00f8448030000498d5f38498d473048895c24284889442420488d4c247041b91800000089fa41b802000000e83a03000085c00f84160300004c89f849c747102000000049c747282800000049c747401800000049c7474800000000418b570885d27e6dffca4881faff010000b9ff010000480f42ca488b10c1e1054883c1204531c00f1f8400000000004e8b0c024e894c00504e8b4c02084e894c0058460fb64c021046894c0060460fb64c021146894c0064460fb64c021246894c006842c744006c000000004983c0204c39c175ba418b0e85c90f8ea5010000ffc94881f9ff010000baff010000480f42d1488b4818488d1492488d14d5280000004531c0662e0f1f8400000000004e8b0c014e898c00504000004e8b4c01084e898c0058400000460fb64c011046888c0060400000460fb64c011146888c0061400000460fb64c011246888c0062400000460fb64c011346888c0063400000460fb64c011446888c0064400000460fb64c011546888c0065400000460fb64c011646888c0066400000460fb64c011746888c0067400000460fb64c011846888c0068400000460fb64c011946888c0069400000460fb64c011a46888c006a400000460fb64c011b46888c006b400000460fb64c011c46888c006c400000460fb64c011d46888c006d400000460fb64c011e46888c006e400000460fb64c011f46888c006f400000460fb64c012046888c0070400000460fb64c012146888c0071400000460fb64c012246888c0072400000460fb64c012346888c0073400000460fb64c012446888c0074400000460fb64c012546888c0075400000460fb64c012646888c0076400000460fb64c012746888c00774000004983c0284c39c20f858afeffff448b034585c00f8e8c00000041ffc84181f8ff000000baff000000410f42d0488b4830ffc231f64585c074704189d14181e1fe0100004531d24531c04e8b1c114e899c10509000004e8b5c11084e899c10589000004e8b5c11104e899c10609000004e8b5c11184e899c10689000004e8b5c11204e899c10709000004e8b5c11284e899c10789000004983c0024983c2304d39c175a6eb0731f6eb324531c0f6c201742a48055090000049c1e0034b8d14404c8b04114c8904104c8b4411084c89441008488b4c111048894c101089f04881c4780200005b5d5f5e415c415d415e415fc3660f1f4400004157415641554154565755534883ec3885d20f8e270500004c894c241889d64889f748c1e70531db41be4000000048c74424200000000048c744242800000000eb1a66666666662e0f1f84000000000048ffc34839f30f84c80400004889da48c1e205488d0411807c11110075068078120074dc4c8b204c8b7808498d4424104c39f877cb4d8b2c24eb2e4c8b0c24488b4424284939c1490f47c14889442428488b442420480f47c5488944242066904883c5184c39fd77974c89e54c89e84983c4084c8b6d084531d2eb0d0f1f40004983c2204c39d774d742807c111100750842807c11120074e74e8b4c11084929c176dd4a39041177d74983f90272b149d1e94983f9404d0f43ce49ffc94989c2450fb71a664585db741f4183c3814983e9010f92c2664183fba172844983c20284d274dce977ffffff4531c9eb0f66904983c1204c39cf0f8463ffffff42807c09100074eb4e396c090876e44e392c0977de0fb7104183f8010f84de0200004585c00f854d0300006683fa410f852effffff66837802620f8523ffffff41b90600000066837804730f8512ffffff6641833c01000f8506ffffff488b4424184801e84883c01041b9010000004c39f80f87cafeffff488b4424184801e84889ea48895424104889c24c8b10488b400848894424084531dbeb140f1f80000000004983c3204c39df0f8492feffff42807c191100750842807c19120074e34a8b4419084c29d076d94e39141977d34883f8020f8268feffff48895424304c890c2448d1e84883f840490f43c648ffc84d89d36666666666662e0f1f840000000000410fb7136685d2742383c2814883e801410f92c16683faa10f821dfeffff4983c3024584c974d9e90ffeffff31c04c8b0c24488b542408eb140f1f80000000004883c0204839c70f84f2fdffff807c01100074ec483954010876e54839140177df4183f80275406641833a590f85620100006641837a02590f85560100006641837a04590f854a0100006641837a06590f853e0100006641837a08000f8532010000e925020000488b442410488b104531c9eb094983c1204c39cf742542807c091100750842807c09120074e74a8b4409084829d076dd4a39140977d748d1e8eb0231c04531c9eb094983c1204c39cf745542807c091100750842807c09120074e74e8b5c09084d29d376dd4e39140977d74d89d949d1e94889542408488d50ff4883fa3f488b542408490f43c64939c14c0f43c84983fb024c0f42c84c894c24104d85c9eb704989d1488d50ff4883fa3f4c89ca490f43c648894424104c8b0c240fb702450fb71a6685c07451664585db744b4889542408418d53bf458d4b2080fa1a410fb6d1410f43d3448d48bf448d58204180f91a450fb6cb440f43c84138d175254983c202488b5424084883c20248ff4c24104c8b0c2475a5eb15664439d8760fe975fcffff4c8b0c240f836bfcffff49ffc1488b5424184c8b542430498d04124c01d24883c2104c39fa4c89d20f8688fdffffe942fcffff6683fa420f8559fcffff668378026c0f854efcffff668378046f0f8543fcffff66837806630f8538fcffff668378086b0f852dfcffff6683780a490f8522fcffff6683780c6e0f8517fcffff6683780e700f850cfcffff66837810750f8501fcffff41b91400000066837812740f85f0fbffffe9d9fcffff6683fa410f85e1fbffff66837802680f85d6fbffff668378046b0f85cbfbffff66837806500f85c0fbffff66837808610f85b5fbffff6683780a740f85aafbffff41b90e0000006683780c680f8599fbffffe982fcffff49ffc1e96bfbffff4c8b4424204d85c00f94c0488b5424284883fa0a0f92c108c1740431c0eb1b488b8424a00000004c8900488b8424a8000000488910b8010000004883c4385b5d5f5e415c415d415e415fc3"


; Decode a raw hex machine-code blob and return an executable Buffer.
MCode(hex) {
    size := StrLen(hex) // 2
    buf := Buffer(size)
    loop size {
        byte := Integer("0x" SubStr(hex, 2 * A_Index - 1, 2))
        NumPut("UChar", byte, buf, A_Index - 1)
    }
    oldProtect := 0
    if !DllCall("VirtualProtect", "Ptr", buf.Ptr, "UPtr", size, "UInt", 0x40, "UInt*", oldProtect)
        throw Error("VirtualProtect failed", -1)
    return buf
}


class AhkMagic {
    static scanner := 0
    static exportScanner := 0
    static inprocEval := 0
    static memScriptLoader := 0
    static internalLocator := 0
    static internalLocated := false
    static evalScriptLocated := false
    static evalLayoutLocated := false
    static evalMFuncsOff := 0
    static evalMFuncsCountOff := 0
    static evalMLastLineOff := 0
    static evalMJumpLineOff := 0
    static evalCurrOff := 0
    static evalStructOffsets := 0
    static evalLayoutArr := 0
    static evalParserLocated := false
    static evalParser := Map()
    static evalPreparse := 0
    static evalPreprocess := 0
    static evalOpenInclude := 0
    static evalLoadTs := 0
    static evalSrcCount := 0
    static evalGptr := 0
    static exprToPostfix := 0
    static expandSingleArg := 0
    static currLineSlot := 0
    static crtFree := 0
    static gScript := 0
    static finalizeExpr := 0
    static findOrAddVar := 0
    static symInvalid := 73
    static moduleBase := 0
    static bifTablePtr := 0
    static bifCount := 0
    static bifStride := 0
    static mdfuncTablePtr := 0
    static mdfuncCount := 0
    static mdfuncStride := 0
    static bivTablePtr := 0
    static bivCount := 0
    static bivStride := 0
    static bif := Map()
    static mdfunc := Map()
    static biv := Map()

    static Init() {
        if AhkMagic.scanner
            return
        if A_PtrSize != 8
            throw Error("this mcode blob is x64 only", -1)
        try {
            AhkMagic._InitTables()
        } catch as e {
            ; A half-initialized state would make later calls fail with
            ; misleading errors; reset so Init can be retried cleanly.
            AhkMagic.scanner := 0
            AhkMagic.bif.Clear()
            AhkMagic.mdfunc.Clear()
            AhkMagic.biv.Clear()
            throw e
        }
    }

    ; Parse the scanner output into bif/mdfunc/biv maps.  The scanner copies
    ; at most 512/512/256 entries into the 64 KiB output buffer; larger
    ; tables are an explicit unsupported-layout error, never a silent read.
    static _InitTables() {
        AhkMagic.scanner := MCode(MC_BIF_SCANNER_X64)
        AhkMagic.moduleBase := DllCall("GetModuleHandle", "Str", A_AhkPath, "Ptr")
        if !AhkMagic.moduleBase
            throw Error("GetModuleHandle failed", -1)

        out := Buffer(64 * 1024)
        rc := DllCall(AhkMagic.scanner.Ptr, "Ptr", AhkMagic.moduleBase, "Ptr", out.Ptr, "Int")
        if rc != 0
            throw Error("AhkScanTables failed with rc=" rc, -1)

        AhkMagic.bifTablePtr := NumGet(out, 0, "Ptr")
        AhkMagic.bifCount := NumGet(out, 8, "Int64")
        AhkMagic.bifStride := NumGet(out, 16, "Int64")
        AhkMagic.mdfuncTablePtr := NumGet(out, 24, "Ptr")
        AhkMagic.mdfuncCount := NumGet(out, 32, "Int64")
        AhkMagic.mdfuncStride := NumGet(out, 40, "Int64")
        AhkMagic.bivTablePtr := NumGet(out, 48, "Ptr")
        AhkMagic.bivCount := NumGet(out, 56, "Int64")
        AhkMagic.bivStride := NumGet(out, 64, "Int64")

        ; The copy loops below run on raw pointer arithmetic without any
        ; buffer object, so validate every count and stride BEFORE looping.
        if AhkMagic.bifCount < 1 or AhkMagic.bifCount > 512
            throw Error("g_BIF table count out of scanner contract: "
                . AhkMagic.bifCount, -1)
        if AhkMagic.mdfuncCount < 1 or AhkMagic.mdfuncCount > 512
            throw Error("sMdFunc table count out of scanner contract: "
                . AhkMagic.mdfuncCount, -1)
        if AhkMagic.bivCount < 1 or AhkMagic.bivCount > 256
            throw Error("g_BIV_A table count out of scanner contract: "
                . AhkMagic.bivCount, -1)
        if AhkMagic.bifStride != 0x20 or AhkMagic.mdfuncStride != 0x28
            or AhkMagic.bivStride != 0x18
            throw Error("unexpected interpreter table strides", -1)

        bifBase := 80
        bifStride := 32
        loop AhkMagic.bifCount {
            p := out.Ptr + bifBase + (A_Index - 1) * bifStride
            namePtr := NumGet(p, 0, "Ptr")
            fnPtr := NumGet(p, 8, "Ptr")
            min := NumGet(p, 16, "UInt")
            max := NumGet(p, 20, "UInt")
            fid := NumGet(p, 24, "UInt")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("g_BIF entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.bif[name] := Map(
                "index", A_Index - 1,
                "rva", fnPtr - AhkMagic.moduleBase,
                "min", min,
                "max", max,
                "fid", fid
            )
        }

        mdfBase := 80 + 512 * 32
        mdfStride := 40
        loop AhkMagic.mdfuncCount {
            p := out.Ptr + mdfBase + (A_Index - 1) * mdfStride
            namePtr := NumGet(p, 0, "Ptr")
            fnPtr := NumGet(p, 8, "Ptr")
            retType := NumGet(p, 16, "UChar")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("sMdFunc entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.mdfunc[name] := Map(
                "index", A_Index - 1,
                "rva", fnPtr - AhkMagic.moduleBase,
                "ret", retType
            )
        }

        bivBase := 80 + 512 * 32 + 512 * 40
        bivStride := 24
        loop AhkMagic.bivCount {
            p := out.Ptr + bivBase + (A_Index - 1) * bivStride
            namePtr := NumGet(p, 0, "Ptr")
            getter := NumGet(p, 8, "Ptr")
            setter := NumGet(p, 16, "Ptr")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("g_BIV_A entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.biv[name] := Map(
                "index", A_Index - 1,
                "getter_rva", getter ? getter - AhkMagic.moduleBase : 0,
                "setter_rva", setter ? setter - AhkMagic.moduleBase : 0
            )
        }
    }

    ; True when [ptr, ptr+size) falls inside committed readable memory.
    ; Used before dereferencing pointers that come from scanned tables.
    static _Readable(ptr, size) {
        if !ptr or ptr >= 0x7FFFFFFFFFFF
            return false
        mbi := Buffer(48)
        if !DllCall("VirtualQuery", "Ptr", ptr, "Ptr", mbi.Ptr, "UPtr", 48)
            return false
        state := NumGet(mbi, 32, "UInt")
        protect := NumGet(mbi, 36, "UInt")
        if state != 0x1000 ; MEM_COMMIT
            return false
        if protect & 0x100 ; PAGE_GUARD
            return false
        readable := (protect & 0x02) or (protect & 0x04) or (protect & 0x08)
            or (protect & 0x20) or (protect & 0x40) or (protect & 0x80)
        if !readable
            return false
        regionBase := NumGet(mbi, 0, "Ptr")
        regionSize := NumGet(mbi, 24, "Ptr")
        return ptr >= regionBase and ptr + size <= regionBase + regionSize
    }

    static BifRva(name) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        return AhkMagic.bif[name]["rva"]
    }

    static BifAddr(name) {
        AhkMagic.Init()
        return AhkMagic.moduleBase + AhkMagic.BifRva(name)
    }

    static PatchBif(name, newName) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        if !AhkMagic.bif.Has(newName)
            throw Error("builtin not found: " newName)
        entryAddr := AhkMagic.bifTablePtr
            + AhkMagic.bif[name]["index"] * AhkMagic.bifStride
            + A_PtrSize
        oldPtr := NumGet(entryAddr, 0, "Ptr")
        expected := AhkMagic.moduleBase + AhkMagic.bif[name]["rva"]
        if oldPtr != expected
            throw Error("table pointer mismatch for " name
                ": got 0x" Format("{:X}", oldPtr)
                ", expected 0x" Format("{:X}", expected))
        NumPut("Ptr", AhkMagic.moduleBase + AhkMagic.bif[newName]["rva"], entryAddr)
        return oldPtr
    }

    static RestoreBif(name, oldPtr) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        entryAddr := AhkMagic.bifTablePtr
            + AhkMagic.bif[name]["index"] * AhkMagic.bifStride
            + A_PtrSize
        NumPut("Ptr", oldPtr, entryAddr)
    }

    ; Deep patch: change the mBIF field of an already-resolved built-in Func
    ; object, so even direct calls compiled at load time (e.g. Abs(1)) are
    ; redirected.  Works together with PatchBif on the table itself.
    static PatchBifObject(fnObj, newName) {
        AhkMagic.Init()
        if !(fnObj is Func)
            throw TypeError("expected a Func object")
        if !AhkMagic.bif.Has(newName)
            throw Error("builtin not found: " newName)
        fnName := fnObj.Name
        if !AhkMagic.bif.Has(fnName)
            throw Error("function object is not a built-in: " fnName)
        oldAddr := AhkMagic.moduleBase + AhkMagic.bif[fnName]["rva"]
        newAddr := AhkMagic.moduleBase + AhkMagic.bif[newName]["rva"]
        rawPtr := ObjPtr(fnObj)
        off := AhkMagic._FindBifPtrOffset(rawPtr, oldAddr)
        if off < 0
            throw Error("could not locate mBIF in function object for " fnName)
        ; Patch the table first, then the object.  If the object write ever
        ; fails the table patch is rolled back, so callers never observe a
        ; half-patched state.
        old := AhkMagic.PatchBif(fnName, newName)
        try
            NumPut("Ptr", newAddr, rawPtr, off)
        catch as e {
            AhkMagic.RestoreBif(fnName, old)
            throw e
        }
        return Map(
            "fnName", fnName,
            "newName", newName,
            "oldPtr", old,
            "offset", off
        )
    }

    static RestoreBifObject(fnObj, state) {
        AhkMagic.Init()
        if !(fnObj is Func)
            throw TypeError("expected a Func object")
        fnName := fnObj.Name
        if state["fnName"] != fnName
            throw Error("state does not match function object " fnName)
        if !AhkMagic.bif.Has(state["newName"])
            throw Error("builtin not found: " state["newName"])
        rawPtr := ObjPtr(fnObj)
        current := AhkMagic.moduleBase + AhkMagic.bif[state["newName"]]["rva"]
        off := AhkMagic._FindBifPtrOffset(rawPtr, current)
        if off < 0
            throw Error("could not locate mBIF in function object for " fnName)
        NumPut("Ptr", state["oldPtr"], rawPtr, off)
        AhkMagic.RestoreBif(fnName, state["oldPtr"])
    }

    static _FindBifPtrOffset(rawPtr, targetAddr) {
        loop 64 {
            off := (A_Index - 1) * 8
            if NumGet(rawPtr, off, "Ptr") = targetAddr
                return off
        }
        return -1
    }

    ; General PE export-table scanner (works for any loaded DLL/EXE module).
    static ScanExports(moduleBase) {
        if !(moduleBase is Integer) or !moduleBase
            throw TypeError("moduleBase must be a nonzero module address", -1)
        if !AhkMagic.exportScanner
            AhkMagic.exportScanner := MCode(MC_PE_EXPORT_SCANNER_X64)
        out := Buffer(100 * 1024)
        rc := DllCall(
            AhkMagic.exportScanner.Ptr,
            "Ptr", moduleBase,
            "Ptr", out.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("AhkScanExports failed with rc=" rc)
        count := NumGet(out, 8, "Int64")
        ; The scanner writes at most 4096 entries into the 100 KiB buffer.
        if count < 0 or count > 4096
            throw Error("export count out of scanner contract: " count, -1)
        result := Map()
        loop count {
            p := out.Ptr + 24 + (A_Index - 1) * 24
            namePtr := NumGet(p, 0, "Ptr")
            fnRva := NumGet(p, 8, "Int64")
            ordinal := NumGet(p, 16, "UInt")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("export entry " A_Index " has an invalid name pointer", -1)
            result[StrGet(namePtr, 1024, "UTF-8")] := Map(
                "rva", fnRva,
                "ordinal", ordinal
            )
        }
        return result
    }

    ; ------------------------------------------------------------------
    ; True in-process Eval.  Locates the interpreter's own expression
    ; compiler/evaluator, builds a temporary Line/ArgStruct, and calls
    ; them directly in this process.
    ; ------------------------------------------------------------------

    static _ModuleSections() {
        base := AhkMagic.moduleBase
        peOff := NumGet(base, 0x3C, "UInt")
        numSections := NumGet(base + peOff + 6, "UShort")
        optSize := NumGet(base + peOff + 20, "UShort")
        secTable := base + peOff + 24 + optSize
        result := Map()
        loop numSections {
            hdr := secTable + (A_Index - 1) * 40
            name := StrGet(hdr, 8, "UTF-8")
            vsize := NumGet(hdr + 8, "UInt")
            va := NumGet(hdr + 12, "UInt")
            result[name] := Map(
                "rva", va,
                "size", vsize,
                "ptr", base + va
            )
        }
        pdata := result.Has(".pdata") ? result[".pdata"] : 0
        for _, sec in result
            sec["pdata"] := pdata
        return result
    }

    static _TextSection(secs) {
        if secs.Has(".text")
            return secs[".text"]
        for name, sec in secs
            if name != ".rsrc"
                return sec
        throw Error("text section not found")
    }

    static _FindUtf16(secs, text) {
        len := StrLen(text)
        needle := Buffer(2 * (len + 1), 0)
        StrPut(text, needle, "UTF-16")
        hits := []
        for name, sec in secs {
            if name = ".rsrc"
                continue
            if !sec["ptr"] or sec["size"] < 2 * len
                continue
            p := sec["ptr"]
            count := sec["size"] >= 2 * len
                ? Min((sec["size"] - 2 * len) // 2 + 1, 0x100000)
                : 0
            if count <= 0
                continue
            loop count {
                off := (A_Index - 1) * 2
                ok := true
                loop len {
                    if NumGet(p + off + 2 * (A_Index - 1), "UShort")
                        != NumGet(needle, 2 * (A_Index - 1), "UShort") {
                        ok := false
                        break
                    }
                }
                if ok and NumGet(p + off + 2 * len, "UShort") = 0
                    hits.Push(sec["rva"] + off)
            }
        }
        return hits
    }

    static _RipRefs(sec, targetRva) {
        p := sec["ptr"]
        size := sec["size"]
        refs := []
        if size < 8
            return refs
        loop size - 7 {
            i := A_Index - 1
            b := NumGet(p + i, "UChar")
            if b != 0x48 and b != 0x4C
                continue
            if NumGet(p + i + 1, "UChar") != 0x8D
                continue
            reg := NumGet(p + i + 2, "UChar")
            if reg != 0x05 and reg != 0x0D and reg != 0x15 and reg != 0x1D
                and reg != 0x25 and reg != 0x2D and reg != 0x35 and reg != 0x3D
                continue
            disp := NumGet(p + i + 3, "Int")
            next := sec["rva"] + i + 7
            if next + disp = targetRva
                refs.Push(sec["rva"] + i)
        }
        return refs
    }

    static _PdataFunctionStart(pdata, rva, sec) {
        if !pdata or pdata["size"] < 12
            throw Error("PE .pdata section is missing", -1)
        p := pdata["ptr"]
        count := pdata["size"] // 12
        lo := 1
        hi := count
        idx := 0
        while lo <= hi {
            mid := (lo + hi) // 2
            off := (mid - 1) * 12
            begin := NumGet(p + off, "UInt")
            end := NumGet(p + off + 4, "UInt")
            if rva < begin {
                hi := mid - 1
                continue
            }
            if rva >= end {
                lo := mid + 1
                continue
            }
            idx := mid
            break
        }
        if !idx
            throw Error("function start not found for RVA "
            . Format("0x{:X}", rva), -1)
        loop idx {
            current := idx - A_Index + 1
            off := (current - 1) * 12
            begin := NumGet(p + off, "UInt")
            if AhkMagic._IsFunctionEntryCode(sec, begin)
                return begin
        }
        throw Error("logical function entry not found for RVA "
            . Format("0x{:X}", rva), -1)
    }

    static _PdataEntryStart(pdata, rva) {
        if !pdata or pdata["size"] < 12
            return 0
        p := pdata["ptr"]
        count := pdata["size"] // 12
        lo := 1
        hi := count
        while lo <= hi {
            mid := (lo + hi) // 2
            off := (mid - 1) * 12
            begin := NumGet(p + off, "UInt")
            end := NumGet(p + off + 4, "UInt")
            if rva < begin {
                hi := mid - 1
                continue
            }
            if rva >= end {
                lo := mid + 1
                continue
            }
            return begin
        }
        return 0
    }

    static _IsFunctionEntryCode(sec, rva) {
        off := rva - sec["rva"]
        if off < 0 or off + 4 > sec["size"]
            return false
        p := sec["ptr"]
        b0 := NumGet(p + off, "UChar")
        b1 := NumGet(p + off + 1, "UChar")
        b2 := NumGet(p + off + 2, "UChar")
        b3 := NumGet(p + off + 3, "UChar")
        if b0 = 0x48 and b1 = 0x89
            and ((b2 = 0x5C and b3 = 0x24)
                or (b2 = 0x54 and b3 = 0x24)
                or (b2 = 0x4C and b3 = 0x24))
            return true
        if b0 = 0x4C and b1 = 0x89
            and ((b2 = 0x44 and b3 = 0x24)
                or (b2 = 0x4C and b3 = 0x24))
            return true
        if b0 = 0x40 and b1 >= 0x50 and b1 <= 0x57
            return true
        if b0 = 0x55 or b0 = 0x53 or b0 = 0x56 or b0 = 0x57
            or b0 = 0x41
            return true
        return false
    }

    static _FnStart(sec, refRva) {
        if !sec.Has("pdata")
            throw Error("section has no PE .pdata context", -1)
        return AhkMagic._PdataFunctionStart(sec["pdata"], refRva, sec)
    }

    static _FindCallers(sec, targetRva) {
        p := sec["ptr"]
        size := sec["size"]
        calls := []
        if size < 6
            return calls
        loop size - 5 {
            i := A_Index - 1
            if NumGet(p + i, "UChar") != 0xE8
                continue
            disp := NumGet(p + i + 1, "Int")
            if sec["rva"] + i + 5 + disp = targetRva
                calls.Push(sec["rva"] + i)
        }
        return calls
    }

    static _BestStart(sec, refs) {
        starts := Map()
        for ref in refs {
            start := AhkMagic._FnStart(sec, ref)
            starts[start] := starts.Has(start) ? starts[start] + 1 : 1
        }
        best := 0
        bestCount := 0
        for start, count in starts {
            if count > bestCount {
                best := start
                bestCount := count
            }
        }
        return best
    }

    static _CurrLineSlotAddr() {
        AhkMagic.Init()
        if !AhkMagic.biv.Has("LineNumber")
            throw Error("A_LineNumber getter not found")
        p := AhkMagic.moduleBase + AhkMagic.biv["LineNumber"]["getter_rva"]
        if NumGet(p, "UChar") != 0x48 or NumGet(p + 1, "UChar") != 0x8B
            or NumGet(p + 2, "UChar") != 0x05
            throw Error("unexpected A_LineNumber getter code")
        disp := NumGet(p + 3, "Int")
        return p + 7 + disp
    }

    static _LocateInternalFunctions() {
        if AhkMagic.internalLocated
            return
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)

        postfixRefs := AhkMagic._RipRefs(text, AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfix2 := AhkMagic._BestStart(text, postfixRefs)
        if !postfix2
            throw Error("ExpressionToPostfix not found")

        expandRefs := AhkMagic._RipRefs(text, AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expand := AhkMagic._BestStart(text, expandRefs)
        if !expand
            throw Error("ExpandExpression not found")

        AhkMagic.exprToPostfix := AhkMagic.moduleBase + postfix2
        AhkMagic.expandSingleArg := AhkMagic.moduleBase + expand
        AhkMagic.currLineSlot := AhkMagic._CurrLineSlotAddr()
        if !AhkMagic.internalLocator
            AhkMagic.internalLocator := MCode(MC_INTERNAL_LOCATOR_X64)
        locOut := Buffer(64, 0)
        rc := DllCall(
            AhkMagic.internalLocator.Ptr,
            "Ptr", AhkMagic.moduleBase,
            "UInt64", text["rva"],
            "UInt64", text["size"],
            "UInt64", postfix2,
            "UInt64", expand,
            "Ptr", locOut.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("internal locator failed with rc=" rc)
        AhkMagic.gScript := NumGet(locOut, 0, "Ptr")
        AhkMagic.finalizeExpr := NumGet(locOut, 8, "Ptr")
        AhkMagic.findOrAddVar := NumGet(locOut, 16, "Ptr")
        AhkMagic.crtFree := NumGet(locOut, 24, "Ptr")
        AhkMagic.symInvalid := NumGet(locOut, 32, "UInt")
        AhkMagic.internalLocated := true
    }

    static _LocateCrtFree() {
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        postfixRva := AhkMagic.exprToPostfix - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, postfixRva)
        for callerRva in callers {
            off := callerRva - text["rva"] + 5
            p := text["ptr"] + off
            loop 96 {
                i := A_Index - 1
                if NumGet(p + i, "UChar") != 0xE8
                    continue
                disp := NumGet(p + i + 1, "Int")
                target := text["rva"] + off + i + 5 + disp
                if target != postfixRva
                    return AhkMagic.moduleBase + target
            }
        }
        throw Error("CRT free not found")
    }

    static _LocatePreprocessFunc(sec) {
        p := sec["ptr"]
        size := sec["size"]
        count := size - 8
        loop count {
            i := A_Index - 1
            if NumGet(p + i, "UChar") != 0x41
                or NumGet(p + i + 1, "UChar") != 0x0F
                or NumGet(p + i + 2, "UChar") != 0xB6
                continue
            m := NumGet(p + i + 3, "UChar")
            if m != 0x76 and m != 0x7E
                continue
            if NumGet(p + i + 4, "UChar") != 0x23
                continue
            limit := Min(size - 4, i + 0x300)
            j := i + 5
            while j < limit {
                if NumGet(p + j, "UChar") = 0x80
                    and NumGet(p + j + 1, "UChar") = 0x78
                    and NumGet(p + j + 2, "UChar") = 0x23
                    and NumGet(p + j + 3, "UChar") = 0x02
                    return AhkMagic._FnStart(sec, sec["rva"] + i)
                j += 1
            }
        }
        return 0
    }

    static _LocateGptr(sec, preparseRva) {
        callers := AhkMagic._FindCallers(sec, preparseRva)
        for caller in callers {
            start := AhkMagic._FnStart(sec, caller)
            p := sec["ptr"]
            off := start - sec["rva"]
            limit := Min(sec["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        b0 := NumGet(p + j, "UChar")
                        b1 := NumGet(p + j + 1, "UChar")
                        b2 := NumGet(p + j + 2, "UChar")
                        b3 := NumGet(p + j + 3, "UChar")
                        if b0 = 0x48
                            and b1 = 0x89
                            and (b2 = 0x58 or b2 = 0x50)
                            and (b3 = 0x28 or b3 = 0x50) {
                            disp := NumGet(p + i + 3, "Int")
                            return sec["rva"] + i + 7 + disp
                        }
                        j += 1
                    }
                }
                i += 1
            }
        }
        return 0
    }

    static _LocateLoadTs(sec, openRva) {
        callers := AhkMagic._FindCallers(sec, openRva)
        for caller in callers {
            p := sec["ptr"]
            base := sec["rva"]
            off := caller - base + 5
            limit := Min(sec["size"] - 5, off + 0x300)
            i := off
            while i < limit {
                isCmp := NumGet(p + i, "UChar") = 0x83
                    and NumGet(p + i + 1, "UChar") = 0xF8
                    and NumGet(p + i + 2, "UChar") = 0x03
                isCmp2 := NumGet(p + i, "UChar") = 0x3D
                    and NumGet(p + i + 1, "UChar") = 0x03
                if isCmp or isCmp2 {
                    j := i + 3
                    callLimit := Min(sec["size"] - 5, j + 0x200)
                    while j < callLimit {
                        if NumGet(p + j, "UChar") = 0xE8 {
                            disp := NumGet(p + j + 1, "Int")
                            target := base + j + 5 + disp
                            if AhkMagic._PdataEntryStart(sec["pdata"], target) = target
                                return target
                        }
                        j += 1
                    }
                    break
                }
                i += 1
            }
        }
        return 0
    }

    static _LocateOpenInclude(secs) {
        text := AhkMagic._TextSection(secs)
        anchors := [
            '%s file "%s" cannot be opened',
            '#Include'
        ]
        for anchor in anchors {
            hit := AhkMagic._FindUtf16(secs, anchor)
            if !hit.Length
                continue
            refs := AhkMagic._RipRefs(text, hit[1])
            start := AhkMagic._BestStart(text, refs)
            if start
                return start
        }
        return 0
    }

    static _LocateSrcCount(sec, openRva) {
        callers := AhkMagic._FindCallers(sec, openRva)
        for caller in callers {
            p := sec["ptr"]
            base := sec["rva"]
            off := caller - base
            i := off - 5
            min := Max(0, off - 0x100)
            while i >= min {
                b := NumGet(p + i, "UChar")
                if b = 0x8B
                    and (NumGet(p + i + 1, "UChar") = 0x2D
                    or NumGet(p + i + 1, "UChar") = 0x1D
                    or NumGet(p + i + 1, "UChar") = 0x35
                    or NumGet(p + i + 1, "UChar") = 0x3D) {
                    disp := NumGet(p + i + 2, "Int")
                    target := base + i + 6 + disp
                    if target > 0x10000 {
                        val := NumGet(AhkMagic.moduleBase + target, "Int")
                        if val >= 0 and val < 100000
                            return target
                    }
                }
                if b = 0x44
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and (NumGet(p + i + 2, "UChar") = 0x2D
                    or NumGet(p + i + 2, "UChar") = 0x3D) {
                    disp := NumGet(p + i + 3, "Int")
                    target := base + i + 7 + disp
                    if target > 0x10000 {
                        val := NumGet(AhkMagic.moduleBase + target, "Int")
                        if val >= 0 and val < 100000
                            return target
                    }
                }
                i -= 1
            }
        }
        return 0
    }

    static _LocateEvalScriptFunctions() {
        if AhkMagic.evalScriptLocated
            return
        AhkMagic._LocateInternalFunctions()
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        postfixRva := AhkMagic.exprToPostfix - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, postfixRva)
        if !callers.Length
            throw Error("PreparseExpressions not found")
        preparse := AhkMagic._FnStart(text, callers[1])

        preprocess := AhkMagic._LocatePreprocessFunc(text)
        if !preprocess
            throw Error("PreprocessLocalVars not found")

        open := AhkMagic._LocateOpenInclude(secs)
        if !open
            throw Error("OpenIncludedFile not found")

        loadTs := AhkMagic._LocateLoadTs(text, open)
        if !loadTs
            throw Error("LoadIncludedFile(TextStream) not found")
        srcCount := 0
        if !RegExMatch(A_AhkVersion, "^2\.1")
            srcCount := AhkMagic._LocateSrcCount(text, open)

        gptr := AhkMagic._LocateGptr(text, preparse)
        if !gptr
            throw Error("g pointer not found")

        AhkMagic.evalPreparse := AhkMagic.moduleBase + preparse
        AhkMagic.evalPreprocess := AhkMagic.moduleBase + preprocess
        AhkMagic.evalOpenInclude := AhkMagic.moduleBase + open
        AhkMagic.evalLoadTs := AhkMagic.moduleBase + loadTs
        AhkMagic.evalSrcCount := srcCount ? AhkMagic.moduleBase + srcCount : 0
        AhkMagic.evalGptr := AhkMagic.moduleBase + gptr
        AhkMagic.evalScriptLocated := true
    }

    static _EvalStructOffsets() {
        if !AhkMagic.evalStructOffsets
            AhkMagic._DiscoverEvalLayout()
        return AhkMagic.evalStructOffsets
    }

    ; Assemble the 21-slot layout array consumed by the eval machine-code
    ; blob.  Every slot comes from runtime discovery; slots with no
    ; discovered equivalent are derived from a discovered anchor and
    ; validated by _ArgShapeInProc/_RemoteArgShape.
    static _BuildEvalLayout(structOffsets, shape) {
        layout := Buffer(21 * 8, 0)
        put := (slot, val) => NumPut("UInt64", val, layout, slot * 8)
        put(0, structOffsets["line_action"])
        put(1, structOffsets["line_argc"])
        put(2, 0)  ; line number: never written, purely cosmetic in errors
        put(3, structOffsets["line_arg"])
        put(4, shape["type"])
        put(5, structOffsets["arg_expression"])
        put(6, shape["len"])
        put(7, shape["text"])
        put(8, shape["deref"])
        put(9, structOffsets["arg_postfix"])
        put(10, shape["max_stack"])
        put(11, shape["max_alloc"])
        put(12, structOffsets["token_value"])
        put(13, structOffsets["token_symbol"])
        put(14, structOffsets["token_stride"])
        put(15, structOffsets["token_usage"])
        put(16, structOffsets["deref_marker"])
        put(17, structOffsets["deref_var"])
        put(18, structOffsets["deref_type"])
        put(19, structOffsets["deref_len"])
        put(20, Max(24, structOffsets["deref_len"] + 4
            , structOffsets["deref_marker"] + 8
            , structOffsets["deref_var"] + 8))
        return layout
    }

    ; Fallback layout for remote evaluation when the target layout has not
    ; been discovered yet.  The blob validates the assumed token layout
    ; before its first write and reports status 9 on a mismatch, so these
    ; defaults can never corrupt a target with a different layout.
    static _DefaultEvalLayout() {
        layout := Buffer(21 * 8, 0)
        vals := [0, 1, 0, 8, 0, 1, 4, 8, 16, 24, 32, 36
            , 0, 16, 24, 8, 0, 8, 16, 20, 24]
        loop 21
            NumPut("UInt64", vals[A_Index], layout, (A_Index - 1) * 8)
        return layout
    }

    ; Derive the ArgStruct fields from the discovered arg_postfix anchor and
    ; validate them against the live arg.  text=postfix-16, len=postfix-12,
    ; deref=postfix-8, type=isExpr-1, max_stack/max_alloc=postfix+8/+12.
    ; Every derived field must be self-consistent on the live arg or
    ; discovery fails loudly.
    static _ArgShapeInProc(argPtr, postfixOff, isexprOff) {
        typeOff := isexprOff - 1
        if typeOff < 0 or NumGet(argPtr, typeOff, "UChar") != 0
            throw Error("arg type field validation failed", -1)
        textOff := postfixOff - 16
        lenOff := postfixOff - 20
        derefOff := postfixOff - 8
        if textOff < 0 or lenOff < 0 or derefOff < 0
            throw Error("arg field derivation out of range", -1)
        textPtr := NumGet(argPtr, textOff, "Ptr")
        if !AhkMagic._Readable(textPtr, 8)
            throw Error("arg text pointer unreadable", -1)
        alen := NumGet(argPtr, lenOff, "UInt")
        if alen = 0 or alen > 4096
            throw Error("arg length field implausible: " alen, -1)
        if StrLen(StrGet(textPtr, alen, "UTF-16")) != alen
            throw Error("arg text/length mismatch", -1)
        dref := NumGet(argPtr, derefOff, "Ptr")
        if dref and !AhkMagic._Readable(dref, 24)
            throw Error("arg deref pointer unreadable", -1)
        return Map(
            "type", typeOff,
            "len", lenOff,
            "text", textOff,
            "deref", derefOff,
            "max_stack", postfixOff + 8,
            "max_alloc", postfixOff + 12)
    }

    ; Remote mirror of _ArgShapeInProc: find a live expression arg among the
    ; discovered functions and validate the derived field offsets by content.
    static _RemoteArgShape(h, layout, s) {
        for q in layout["funcs"] {
            try
                jump := AhkMagic._RPtr(h, q + layout["mjump_line_off"])
            catch
                continue
            if !jump
                continue
            try
                argc := NumGet(AhkMagic._RemoteRead(h, jump + s["line_argc"], 1)
                    , 0, "UChar")
            catch
                continue
            if !argc
                continue
            try
                argPtr := AhkMagic._RPtr(h, jump + s["line_arg"])
            catch
                continue
            try
                postfix := AhkMagic._RPtr(h, argPtr + s["arg_postfix"])
            catch
                continue
            if !postfix
                continue
            try {
                typeOff := s["arg_expression"] - 1
                if typeOff < 0
                    continue
                if NumGet(AhkMagic._RemoteRead(h, argPtr + typeOff, 1)
                    , 0, "UChar") != 0
                    continue
                textOff := s["arg_postfix"] - 16
                lenOff := s["arg_postfix"] - 20
                derefOff := s["arg_postfix"] - 8
                if textOff < 0 or lenOff < 0 or derefOff < 0
                    continue
                textPtr := AhkMagic._RPtr(h, argPtr + textOff)
                alen := NumGet(AhkMagic._RemoteRead(h, argPtr + lenOff, 4)
                    , 0, "UInt")
                if alen = 0 or alen > 4096
                    continue
                if StrLen(AhkMagic._RemoteReadString(h, textPtr, alen))
                    != alen
                    continue
                dref := AhkMagic._RPtr(h, argPtr + derefOff)
                if dref
                    AhkMagic._RemoteRead(h, dref, 24)
                return Map(
                    "type", typeOff,
                    "len", lenOff,
                    "text", textOff,
                    "deref", derefOff,
                    "max_stack", s["arg_postfix"] + 8,
                    "max_alloc", s["arg_postfix"] + 12)
            } catch
                continue
        }
        throw Error("remote arg shape not found", -1)
    }

    static _DiscoverCurrOff() {
        if AhkMagic.evalCurrOff
            return AhkMagic.evalCurrOff
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        preparseRva := AhkMagic.evalPreparse - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, preparseRva)
        for caller in callers {
            p := text["ptr"]
            base := text["rva"]
            off := AhkMagic._FnStart(text, caller) - base
            limit := Min(text["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        if NumGet(p + j, "UChar") = 0x48
                            and (NumGet(p + j + 1, "UChar") = 0x89)
                            and (NumGet(p + j + 2, "UChar") = 0x58
                                or NumGet(p + j + 2, "UChar") = 0x50) {
                            AhkMagic.evalCurrOff := NumGet(p + j + 3, "UChar")
                            return AhkMagic.evalCurrOff
                        }
                        j += 1
                    }
                }
                i += 1
            }
        }
        throw Error("g->curr offset not found")
    }

    static _DiscoverEvalLayout() {
        if AhkMagic.evalLayoutLocated
            return
        AhkMagic._LocateEvalScriptFunctions()
        AhkMagic._DiscoverCurrOff()
        gScript := AhkMagic.gScript
        g := NumGet(AhkMagic.evalGptr, "Ptr")
        savedProbeCur := NumGet(g, AhkMagic.evalCurrOff, "Ptr")
        gSnap := Buffer(0x100)
        DllCall("RtlMoveMemory", "Ptr", gSnap.Ptr, "Ptr", g, "UPtr", 0x100)
        NumPut("Ptr", 0, g, AhkMagic.evalCurrOff)
        snap := Buffer(0x200)
        DllCall("RtlMoveMemory", "Ptr", snap.Ptr, "Ptr", gScript, "UPtr", 0x200)
        oldSrcCount := AhkMagic.evalSrcCount ? NumGet(AhkMagic.evalSrcCount, "Int") : 0

        ; The probe function name is randomized so it can never collide with
        ; a function the host script already defines.
        probeName := "ahkHackLayoutProbe" Format("{:x}", A_TickCount)
        probe := probeName "() {`n    return A_Args.Length + 1`n}`n"

        ; The probe really loads a function and mutates interpreter state.
        ; Every failure below must restore the snapshot, otherwise the host
        ; interpreter is left with a stray function, a cleared g->curr and a
        ; modified source count.
        try {
            AhkMagic._LoadScriptMemory(probe)

            countOff := 0
            loop 0x200 // 4 {
                off := (A_Index - 1) * 4
                before := NumGet(snap, off, "Int")
                after := NumGet(gScript, off, "Int")
                if after = before + 1 and before >= 0 and after > 0 {
                    countOff := off
                    break
                }
            }
            if !countOff
                throw Error("probe load did not register a function: "
                    . "TextStream layout validation failed (mFuncsCount "
                    . "offset not found after rc=1 load)", -1)
            oldCount := NumGet(snap, countOff, "Int")

            funcsOff := 0
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                ; gScript[0] is the variable array on every known build, and
                ; funcsOff=0 is also the "not found" sentinel below, so it
                ; must never participate in the match.
                if off = 0
                    continue
                p := NumGet(gScript, off, "Ptr")
                if !(p > 0x10000 and p < 0x7fffffffffff)
                    continue
                if DllCall("IsBadReadPtr", "Ptr", p, "UPtr", 0x100)
                    continue
                newFunc := NumGet(p, oldCount * 8, "Ptr")
                ; The new entry plus its two predecessors must all look like
                ; 8-aligned objects: unrelated heap garbage adjacent to other
                ; arrays must not impersonate mFuncs.
                ok := newFunc > 0x10000 and newFunc < 0x7fffffffffff
                    and Mod(newFunc, 8) = 0
                if ok and oldCount >= 2 {
                    prev := NumGet(p, (oldCount - 1) * 8, "Ptr")
                    prev2 := NumGet(p, (oldCount - 2) * 8, "Ptr")
                    ok := prev > 0x10000 and prev < 0x7fffffffffff
                        and Mod(prev, 8) = 0
                        and prev2 > 0x10000 and prev2 < 0x7fffffffffff
                        and Mod(prev2, 8) = 0
                }
                if ok {
                    funcsOff := off
                    break
                }
            }
            if !funcsOff
                throw Error("mFuncs offset not found")

            lastOff := -1
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                if off = funcsOff
                    continue
                before := NumGet(snap, off, "Ptr")
                after := NumGet(gScript, off, "Ptr")
                if before != after and after > 0x10000 {
                    lastOff := off
                    break
                }
            }
            if lastOff < 0 {
                detail := "count=" countOff " funcs=" funcsOff
                loop 0x200 // 8 {
                    off := (A_Index - 1) * 8
                    b := NumGet(snap, off, "Ptr")
                    a := NumGet(gScript, off, "Ptr")
                    if b != a
                        detail .= " @" Format("{:X}", off) " " b "->" a
                            . " bad=" DllCall("IsBadReadPtr", "Ptr", a, "UPtr", 0x40)
                }
                throw Error("mLastLine offset not found: " detail)
            }

            arrPtr := NumGet(gScript, funcsOff, "Ptr")
            newFunc := NumGet(arrPtr, oldCount * 8, "Ptr")
            oldLast := NumGet(snap, lastOff, "Ptr")
            newLast := NumGet(gScript, lastOff, "Ptr")
            jumpOff := 0
            jumpLine := 0
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                p := NumGet(newFunc, off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                try {
                    lineData := Buffer(0x100)
                    DllCall("RtlMoveMemory", "Ptr", lineData.Ptr, "Ptr", p
                        , "UPtr", 0x100)
                } catch
                    continue
                found := false
                loop lineData.Size // 8 {
                    if NumGet(lineData, (A_Index - 1) * 8, "Ptr") = newFunc {
                        found := true
                        break
                    }
                }
                if found {
                    jumpOff := off
                    jumpLine := p
                    break
                }
            }
            if !jumpOff
                throw Error("mJumpLine offset not found")

            structOffsets := AhkMagic._DiscoverInProcStructs(
                gScript, arrPtr, oldCount, newFunc, jumpOff)

            ; Derive and validate the remaining ArgStruct fields from the
            ; probe's own expression arg, then assemble the layout array the
            ; eval blob consumes.
            probeArg := NumGet(jumpLine, structOffsets["line_arg"], "Ptr")
            if !probeArg
                throw Error("probe arg not found", -1)
            argShape := AhkMagic._ArgShapeInProc(probeArg
                , structOffsets["arg_postfix"]
                , structOffsets["arg_expression"])

            AhkMagic.evalStructOffsets := structOffsets
            AhkMagic.evalLayoutArr := AhkMagic._BuildEvalLayout(
                structOffsets, argShape)
            AhkMagic.evalMFuncsOff := funcsOff
            AhkMagic.evalMFuncsCountOff := countOff
            AhkMagic.evalMLastLineOff := lastOff
            AhkMagic.evalMJumpLineOff := jumpOff
        } finally {
            ; Always roll the interpreter back to the pre-probe snapshot,
            ; whether discovery succeeded or not.
            DllCall("RtlMoveMemory", "Ptr", gScript, "Ptr", snap.Ptr, "UPtr", 0x200)
            if AhkMagic.evalSrcCount
                NumPut("Int", oldSrcCount, AhkMagic.evalSrcCount)
            DllCall("RtlMoveMemory", "Ptr", g, "Ptr", gSnap.Ptr, "UPtr", 0x100)
            NumPut("Ptr", savedProbeCur, g, AhkMagic.evalCurrOff)
        }

        AhkMagic._DiscoverParserOffsets()
        AhkMagic.evalLayoutLocated := true
    }

    ; Discover the parser-state field offsets in THIS process.  Both known
    ; region candidates (sentinel-anchored and qCmp-anchored) are built and
    ; validated against the live Script object; the strictly-validating
    ; candidate wins.  No version family is assumed.
    static _DiscoverParserOffsets() {
        if AhkMagic.evalParserLocated
            return
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        p := text["ptr"]
        base := text["rva"]
        off := AhkMagic.evalLoadTs - AhkMagic.moduleBase - base
        limit := Min(text["size"] - 8, off + 0x100)
        qCmp := -1
        dCmp := -1
        i := off
        while i < limit {
            b0 := NumGet(p + i, "UChar")
            if b0 = 0x48 and NumGet(p + i + 1, "UChar") = 0x83 {
                modrm := NumGet(p + i + 2, "UChar")
                if modrm = 0x79 and NumGet(p + i + 4, "UChar") = 0 {
                    if qCmp < 0
                        qCmp := NumGet(p + i + 3, "UChar")
                } else if modrm = 0xB9 and NumGet(p + i + 7, "UChar") = 0 {
                    if qCmp < 0
                        qCmp := NumGet(p + i + 3, "Int")
                }
            }
            if b0 = 0x83 and NumGet(p + i + 1, "UChar") = 0xB9
                and (i = off or NumGet(p + i - 1, "UChar") != 0x48)
                and NumGet(p + i + 6, "UChar") = 0 {
                if dCmp < 0
                    dCmp := NumGet(p + i + 2, "Int")
            }
            if qCmp >= 0 and dCmp >= 0
                break
            i += 1
        }
        if qCmp < 0 or dCmp < 0
            throw Error("parser state anchors not found")

        ; Validate both candidates against the live Script object and pick
        ; the strictly-validating one.
        gScript := AhkMagic.gScript
        buf := Buffer(0x800)
        DllCall("RtlMoveMemory", "Ptr", buf.Ptr, "Ptr", gScript, "UPtr", 0x800)
        candidates := []
        if dCmp > 0 and dCmp + 4 <= buf.Size
            and NumGet(buf, dCmp, "Int") >= 0
            and NumGet(buf, dCmp, "Int") <= 100000 {
            exprIndexOff := 0
            loop Min(0x40, dCmp) // 4 {
                coff := dCmp - (A_Index - 1) * 4
                if coff + 8 > buf.Size
                    continue
                if NumGet(buf, coff, "Int") = 0x7fffffff
                    and NumGet(buf, coff + 4, "Int") = 0 {
                    exprIndexOff := coff + 4
                    break
                }
            }
            if exprIndexOff and exprIndexOff + 8 <= buf.Size
                candidates.Push(Map(
                    "rich", true,
                    "fields", Map(
                        "mclass_count", dCmp,
                        "mline_parent", exprIndexOff - 0x28,
                        "mpending_related", exprIndexOff - 0x20,
                        "mlast_param_init", exprIndexOff - 0x18,
                        "mpending_hotkey", exprIndexOff - 0x10,
                        "mexpr_func", exprIndexOff - 8,
                        "mexpr_func_index", exprIndexOff,
                        "mnext_func_body", exprIndexOff + 4,
                        "mignore_block", exprIndexOff + 5,
                        "mbackcompat", exprIndexOff + 6,
                        "mcurrent_module", exprIndexOff - 0x48,
                        "mlast_module", exprIndexOff - 0x40)))
        }
        if qCmp > 0 and qCmp + 0x28 <= buf.Size
            candidates.Push(Map(
                "fields", Map(
                    "mopen", qCmp,
                    "mpending_parent", qCmp + 8,
                    "mpending_related", qCmp + 16,
                    "mlast_param_init", qCmp + 24,
                    "mnext_func_body", qCmp + 32,
                    "mclass_count", dCmp)))

        best := 0
        bestScore := -1
        for cand in candidates {
            fields := cand["fields"]
            valid := true
            for key, foff in fields {
                ; Value-level checks are deliberately absent: the parser
                ; fields are saved and restored verbatim, so any plausible
                ; offset round-trips harmlessly.  The only hard requirement
                ; is that every offset stays inside the Script object.
                if !(foff is Integer) or foff <= 0 or foff >= 0x800
                    or foff + 8 > buf.Size {
                    valid := false
                    break
                }
            }
            if valid {
                ; The sentinel-anchored candidate has the richer field set
                ; and only exists when its anchor was actually found.
                score := cand.Has("rich") ? 10 : 0
                if score > bestScore {
                    bestScore := score
                    best := cand["fields"]
                }
            }
        }
        if !best
            throw Error("parser state layout candidates all failed validation"
                , -1)
        AhkMagic.evalParser := best
        AhkMagic.evalParserLocated := true
    }

    static EvalNative(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)
        if !AhkMagic.inprocEval
            AhkMagic.inprocEval := MCode(MC_INPROC_EVAL_X64)
        AhkMagic._LocateInternalFunctions()
        ; The eval blob takes every Line/ArgStruct/token/deref offset from a
        ; runtime-discovered layout array; discover it once and reuse.
        AhkMagic._DiscoverEvalLayout()
        layoutArr := AhkMagic.evalLayoutArr
        if !layoutArr
            throw Error("eval layout not discovered", -1)

        scratch := Buffer(8 * 1024 * 1024, 0)
        out := Buffer(512, 0)
        exprBuf := Buffer((StrLen(expr) + 1) * 2, 0)
        StrPut(expr, exprBuf, "UTF-16")
        rc := DllCall(
            AhkMagic.inprocEval.Ptr,
            "Ptr", AhkMagic.exprToPostfix,
            "Ptr", AhkMagic.expandSingleArg,
            "Ptr", AhkMagic.currLineSlot,
            "Ptr", scratch.Ptr,
            "Ptr", exprBuf.Ptr,
            "Ptr", out.Ptr,
            "Int", 1,
            "Ptr", 0,
            "Ptr", 0,
            "Ptr", AhkMagic.gScript,
            "Ptr", AhkMagic.finalizeExpr,
            "Ptr", AhkMagic.findOrAddVar,
            "Ptr", AhkMagic.crtFree,
            "Ptr", AhkMagic.symInvalid,
            "Ptr", layoutArr.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("EvalNative harness failed with rc=" rc)
        status := NumGet(out, 0, "UInt")
        if status = 9
            throw Error("EvalNative token layout mismatch; "
                . "unsupported runtime layout", -1)
        if status = 10
            throw Error("EvalNative layout validation failed", -1)
        if status != 0
            throw Error("EvalNative failed with status=" status)
        type := NumGet(out, 4, "UInt")
        if type = 1
            return NumGet(out, 8, "Int64")
        if type = 2
            return NumGet(out, 8, "Double")
        if type = 0
            return StrGet(NumGet(out, 16, "Ptr"), "UTF-16")
        if type = 5 {
            ; SYM_OBJECT: wrap the interpreter's live object.  The eval
            ; harness never finalizes its result token, so the object stays
            ; alive; the reference returned here belongs to the caller.
            ptr := NumGet(out, 8, "Ptr")
            if !ptr
                throw Error("EvalNative returned a null object", -1)
            return ObjFromPtrAddRef(ptr)
        }
        throw Error("EvalNative returned unknown symbol " type)
    }

    static Summary() {
        AhkMagic.Init()
        return Format(
            "module=0x{:X}`nbif={} @0x{:X} (stride 0x{:X})`n"
            . "mdfunc={} @0x{:X} (stride 0x{:X})`n"
            . "biv={} @0x{:X} (stride 0x{:X})",
            AhkMagic.moduleBase,
            AhkMagic.bifCount, AhkMagic.bifTablePtr, AhkMagic.bifStride,
            AhkMagic.mdfuncCount, AhkMagic.mdfuncTablePtr, AhkMagic.mdfuncStride,
            AhkMagic.bivCount, AhkMagic.bivTablePtr, AhkMagic.bivStride
        )
    }

    static Eval(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if expr = ""
            throw ValueError("expr must not be empty", -1)
        if RegExMatch(expr, "[\r\n]")
            return AhkMagic.EvalScript(expr)
        try {
            return AhkMagic.EvalNative(expr)
        } catch as e {
            ; Expressions the in-process pipeline cannot evaluate yet fall
            ; back to the explicit subprocess implementation.
        }
        return AhkMagic.EvalSubprocess(expr)
    }

    static EvalScript(text) {
        AhkMagic.Init()
        if !(text is String)
            throw TypeError("text must be a string", -1)
        if Trim(text) = ""
            throw ValueError("text must not be empty", -1)
        ; Pre-check with the interpreter's own /validate switch so a syntax
        ; error becomes a clean library error carrying the real parse
        ; message.  The validator captures its output and kills its process
        ; tree on timeout, so it never leaves a dialog on screen; the loader
        ; rc check below remains as the fallback guard.  A compiled host has
        ; no separate interpreter binary, so it keeps the fallback only.
        if !A_IsCompiled
            AhkMagic._ValidateScript(text, A_AhkPath)

        AhkMagic._DiscoverEvalLayout()
        off := AhkMagic._EvalStructOffsets()
        currOff := AhkMagic.evalCurrOff
        mfuncsOff := AhkMagic.evalMFuncsOff
        mfuncsCountOff := AhkMagic.evalMFuncsCountOff
        mlastLineOff := AhkMagic.evalMLastLineOff
        mjumpLineOff := AhkMagic.evalMJumpLineOff

        last := ""
        for line in StrSplit(text, "`n", "`r") {
            t := Trim(line)
            if t = ""
                continue
            if RegExMatch(t, "^(if|else|for|while|loop|try|catch|finally|return|break|continue|class|static|global|local|throw)\b")
                continue
            if SubStr(t, -1) = "{"
                continue
            last := t
        }
        if last = ""
            throw ValueError("no expression result found in script text", -1)

        gScript := AhkMagic.gScript
        oldLast := NumGet(gScript, mlastLineOff, "Ptr")
        oldFuncCount := NumGet(gScript, mfuncsCountOff, "Int")
        g := NumGet(AhkMagic.evalGptr, "Ptr")
        savedCur := NumGet(g, currOff, "Ptr")
        parser := AhkMagic.evalParser
        savedState := []
        for key in ["mopen", "mpending_parent", "mline_parent", "mpending_related"
            , "mlast_param_init", "mpending_hotkey", "mexpr_func", "mcurrent_module"] {
            if parser.Has(key)
                savedState.Push([key, "Ptr", NumGet(gScript, parser[key], "Ptr")])
        }
        for key in ["mexpr_func_index", "mclass_count"] {
            if parser.Has(key)
                savedState.Push([key, "Int", NumGet(gScript, parser[key], "Int")])
        }
        for key in ["mnext_func_body", "mignore_block", "mbackcompat"] {
            if parser.Has(key)
                savedState.Push([key, "UChar", NumGet(gScript, parser[key], "UChar")])
        }
        try {
            NumPut("Ptr", 0, g, currOff)

            if !AhkMagic.memScriptLoader
                AhkMagic.memScriptLoader := MCode(MC_MEM_SCRIPT_X64)
            textBuf := Buffer((StrLen(text) + 1) * 2, 0)
            StrPut(text, textBuf, "UTF-16")
            scratch := Buffer(0x400, 0)
            rc := DllCall(AhkMagic.memScriptLoader.Ptr
                , "Ptr", AhkMagic.evalLoadTs
                , "Ptr", gScript
                , "Ptr", AhkMagic.evalSrcCount
                , "Ptr", textBuf.Ptr
                , "UInt", StrLen(text) * 2
                , "Ptr", scratch.Ptr
                , "Int")
            if rc != 0
                throw Error("LoadIncludedFile(memory) failed with rc=" rc)

            funcsItem := NumGet(gScript, mfuncsOff, "Ptr")
            funcCount := NumGet(gScript, mfuncsCountOff, "Int")
            ; The count drives raw pointer walks below; refuse values that
            ; cannot come from one script injection.
            if funcCount < oldFuncCount
                or funcCount > oldFuncCount + 4096
                throw Error("mFuncs count changed unexpectedly: "
                    . oldFuncCount " -> " funcCount, -1)
            if funcCount > oldFuncCount {
                firstNew := oldLast ? NumGet(oldLast, off["line_next"], "Ptr") : 0
                if firstNew {
                    rcTail := DllCall(AhkMagic.evalPreparse
                        , "Ptr", gScript
                        , "Ptr", firstNew
                        , "Int")
                    if rcTail != 1
                        throw Error("PreparseExpressions(tail) failed with rc=" rcTail)
                }
                loop funcCount - oldFuncCount {
                    idx := oldFuncCount + A_Index - 1
                    newFunc := NumGet(funcsItem + idx * 8, "Ptr")
                    jump := NumGet(newFunc, mjumpLineOff, "Ptr")
                    if !jump
                        continue

                    rc2 := DllCall(AhkMagic.evalPreparse
                        , "Ptr", gScript
                        , "Ptr", jump
                        , "Int")
                    if rc2 != 1
                        throw Error("PreparseExpressions failed with rc=" rc2)

                    NumPut("Ptr", newFunc, g, currOff)
                    line := jump
                    lineSteps := 0
                    while line {
                        if lineSteps > 100000
                            throw Error("line chain too long; layout likely wrong", -1)
                        lineSteps += 1
                        if NumGet(line, off["line_action"], "UChar") = 3
                            and NumGet(line, off["line_attribute"], "Ptr")
                            NumPut("Ptr", NumGet(line, off["line_attribute"], "Ptr")
                                , g, currOff)
                        argc := NumGet(line, off["line_argc"], "UChar")
                        if argc {
                            arg := NumGet(line, off["line_arg"], "Ptr")
                            if arg and NumGet(arg, off["arg_expression"]
                                , "UChar") {
                                postfix := NumGet(arg, off["arg_postfix"], "Ptr")
                                if postfix {
                                    tokenSteps := 0
                                    while NumGet(postfix, off["token_symbol"], "UInt") != AhkMagic.symInvalid {
                                        if tokenSteps > 100000
                                            throw Error("postfix token chain too long; layout likely wrong", -1)
                                        tokenSteps += 1
                                        if NumGet(postfix, off["token_symbol"], "UInt") = 4
                                            and NumGet(postfix, off["token_usage"], "UInt") < 3 {
                                            deref := NumGet(postfix, off["token_value"], "Ptr")
                                            if deref {
                                                derefType := NumGet(deref
                                                    , off["deref_type"], "UChar")
                                                marker := NumGet(deref, off["deref_marker"], "Ptr")
                                                len := NumGet(deref, off["deref_len"], "UInt")
                                                if derefType = 7 {
                                                    NumPut("Ptr", NumGet(deref
                                                        , off["deref_var"], "Ptr")
                                                        , postfix, off["token_value"])
                                                } else if derefType = 0 and marker and len > 0 and len <= 64 {
                                                    var := DllCall(AhkMagic.findOrAddVar
                                                        , "Ptr", gScript
                                                        , "Ptr", marker
                                                        , "UPtr", len
                                                        , "UInt", 0x103
                                                        , "Ptr")
                                                    if var
                                                        NumPut("Ptr", var, postfix, off["token_value"])
                                                }
                                            }
                                        }
                                        postfix += off["token_stride"]
                                    }
                                }
                            }
                        }
                        line := NumGet(line, off["line_next"], "Ptr")
                    }
                    NumPut("Ptr", savedCur, g, currOff)
                    rc4 := DllCall(AhkMagic.evalPreprocess
                        , "Ptr", gScript
                        , "Ptr", newFunc
                        , "Int")
                    if rc4 != 1
                        throw Error("PreprocessLocalVars failed with rc=" rc4)
                }
            }
            ; Detach the newly added lines from the running line list so the
            ; active execution cannot continue into them.  The new function
            ; remains reachable through mFuncs.  The tail was already
            ; preparsed through firstNew above, so detaching now is safe.
            if oldLast and off.Has("line_next") and mlastLineOff >= 0 {
                NumPut("Ptr", 0, oldLast, off["line_next"])
                NumPut("Ptr", oldLast, gScript, mlastLineOff)
            }
            NumPut("Ptr", 0, g, currOff)
            r := AhkMagic.EvalNative(last)
            NumPut("Ptr", savedCur, g, currOff)
            return r
        } finally {
            for item in savedState
                NumPut(item[2], item[3], gScript, parser[item[1]])
            NumPut("Ptr", savedCur, g, currOff)
        }
    }

    static _LoadScriptMemory(text) {
        if !AhkMagic.memScriptLoader
            AhkMagic.memScriptLoader := MCode(MC_MEM_SCRIPT_X64)
        gScript := AhkMagic.gScript
        textBuf := Buffer((StrLen(text) + 1) * 2, 0)
        StrPut(text, textBuf, "UTF-16")
        scratch := Buffer(0x400, 0)
        rc := DllCall(AhkMagic.memScriptLoader.Ptr
            , "Ptr", AhkMagic.evalLoadTs
            , "Ptr", gScript
            , "Ptr", AhkMagic.evalSrcCount
            , "Ptr", textBuf.Ptr
            , "UInt", StrLen(text) * 2
            , "Ptr", scratch.Ptr
            , "Int")
        if rc != 0
            throw Error("LoadIncludedFile(memory) failed with rc=" rc)
    }
    ; ------------------------------------------------------------------
    ; Remote attach: read the three interpreter tables from another
    ; AutoHotkey process without injecting anything.
    ; ------------------------------------------------------------------

    static _RemoteOpen(pid, write := false) {
        access := 0x410
        if write
            access := 0x410 | 0x28
        h := DllCall("OpenProcess", "UInt", access, "Int", 0, "UInt", pid, "Ptr")
        if !h
            throw Error("OpenProcess failed", -1)
        return h
    }

    static _RemoteModuleBase(h, pid) {
        hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x18, "UInt", pid, "Ptr")
        if hSnap = -1 or !hSnap
            throw Error("CreateToolhelp32Snapshot failed", -1)
        buf := Buffer(1080)
        NumPut("UInt", 1080, buf, 0)
        best := Map("base", 0, "path", "")
        try {
            if !DllCall("Module32FirstW", "Ptr", hSnap, "Ptr", buf.Ptr)
                throw Error("Module32FirstW failed", -1)
            loop {
                modBase := NumGet(buf, 24, "Ptr")
                modName := StrGet(buf.Ptr + 48, 256, "UTF-16")
                modPath := StrGet(buf.Ptr + 560, 260, "UTF-16")
                if !best["base"] or InStr(modName, "AutoHotkey", false) {
                    best := Map("base", modBase, "path", modPath)
                    if InStr(modName, "AutoHotkey", false)
                        break
                }
                if !DllCall("Module32NextW", "Ptr", hSnap, "Ptr", buf.Ptr)
                    break
            }
        } finally {
            DllCall("CloseHandle", "Ptr", hSnap)
        }
        if !best["base"]
            throw Error("module base not found", -1)
        return best
    }

    static _RemotePidByName(name) {
        hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x2, "UInt", 0, "Ptr")
        if hSnap = -1 or !hSnap
            throw Error("CreateToolhelp32Snapshot failed", -1)
        buf := Buffer(568)
        NumPut("UInt", 568, buf, 0)
        pid := 0
        try {
            if !DllCall("Process32FirstW", "Ptr", hSnap, "Ptr", buf.Ptr)
                throw Error("Process32FirstW failed", -1)
            loop {
                exe := StrGet(buf.Ptr + 44, 260, "UTF-16")
                if StrCompare(exe, name, false) = 0 {
                    pid := NumGet(buf, 8, "UInt")
                    break
                }
                if !DllCall("Process32NextW", "Ptr", hSnap, "Ptr", buf.Ptr)
                    break
            }
        } finally {
            DllCall("CloseHandle", "Ptr", hSnap)
        }
        if !pid
            throw Error("process not found: " name, -1)
        return pid
    }

    static _RemoteRead(h, addr, size) {
        buf := Buffer(size)
        read := 0
        if !DllCall("ReadProcessMemory", "Ptr", h, "Ptr", addr, "Ptr", buf.Ptr
            , "UPtr", size, "UPtr*", &read)
            throw Error("ReadProcessMemory failed at 0x" Format("{:X}", addr), -1)
        ; A partial success silently returns a half-garbage buffer and
        ; corrupts every downstream parse; fail loudly instead.
        if read != size
            throw Error("ReadProcessMemory short read at 0x"
                . Format("{:X}", addr) ": " read "/" size, -1)
        return buf
    }

    static _RemoteSections(h, base) {
        dos := AhkMagic._RemoteRead(h, base, 0x40)
        e_lfanew := NumGet(dos, 0x3C, "UInt")
        pe := AhkMagic._RemoteRead(h, base + e_lfanew, 0x18)
        if NumGet(pe, 0, "UInt") != 0x4550
            throw Error("PE signature not found", -1)
        num := NumGet(pe, 6, "UShort")
        optSize := NumGet(pe, 20, "UShort")
        secTable := base + e_lfanew + 24 + optSize
        hdrs := AhkMagic._RemoteRead(h, secTable, num * 40)
        secs := Map()
        hasText := false
        hasData := false
        hasPdata := false
        loop num {
            off := (A_Index - 1) * 40
            name := StrGet(hdrs.Ptr + off, 8, "UTF-8")
            vsize := NumGet(hdrs, off + 8, "UInt")
            va := NumGet(hdrs, off + 12, "UInt")
            rawSize := NumGet(hdrs, off + 16, "UInt")
            size := vsize ? vsize : rawSize
            if name = ".text"
                hasText := true
            if name = ".rdata" or name = ".data"
                hasData := true
            if name = ".pdata"
                hasPdata := true
            secs[name] := Map("name", name, "rva", va, "size", size
                , "start", base + va, "end", base + va + size
                , "data", Buffer(0), "ptr", 0)
        }
        strict := hasText and hasData
        for _, sec in secs {
            if strict and sec["name"] != ".text"
                and sec["name"] != ".rdata" and sec["name"] != ".data"
                continue
            if sec["name"] = ".rsrc"
                continue
            if sec["size"] > 0 and sec["size"] < 0x800000 {
                try
                    sec["data"] := AhkMagic._RemoteRead(h, sec["start"], sec["size"])
                catch
                    sec["data"] := Buffer(0)
                sec["ptr"] := sec["data"].Ptr
            }
        }
        pdata := 0
        if hasPdata {
            sec := secs[".pdata"]
            if sec["size"] > 0 and sec["size"] < 0x800000 {
                try {
                    sec["data"] := AhkMagic._RemoteRead(h, sec["start"]
                        , sec["size"])
                    sec["ptr"] := sec["data"].Ptr
                    pdata := sec
                } catch
                    pdata := 0
            }
        }
        for _, sec in secs
            sec["pdata"] := pdata
        return secs
    }

    static _RemoteReadUtf16(secs, addr, maxLen := 64) {
        for _, sec in secs {
            if addr >= sec["start"] and addr < sec["end"]
                and sec["data"].Size > 0 {
                off := addr - sec["start"]
                if off < sec["data"].Size
                    return StrGet(sec["data"].Ptr + off, maxLen, "UTF-16")
            }
        }
        return ""
    }

    static _RemoteStrValid(secs, addr) {
        text := AhkMagic._RemoteReadUtf16(secs, addr, 64)
        if text = "" or StrLen(text) > 64
            return false
        return RegExMatch(text, "^[\x20-\x7E]+$") ? true : false
    }

    static _RemotePtrInText(secs, ptr) {
        hasText := false
        for _, sec in secs
            if sec["name"] = ".text"
                hasText := true
        for _, sec in secs {
            if ptr < sec["start"] or ptr >= sec["end"]
                continue
            if sec["name"] = ".text"
                return true
            if !hasText and sec["name"] != ".rsrc"
                return true
        }
        return false
    }

    static _RemoteTable(h, secs, base, kind) {
        if kind = "bif" {
            stride := 0x20
            anchor := "Abs"
        } else if kind = "mdfunc" {
            stride := 0x28
            anchor := "BlockInput"
        } else {
            stride := 0x18
            anchor := "AhkPath"
        }
        bestStart := 0
        bestCount := 0
        for _, sec in secs {
            if sec["data"].Size < 32
                continue
            p := sec["data"].Ptr
            maxOff := sec["data"].Size - 16
            off := 0
            while off <= maxOff {
                namePtr := NumGet(p + off, "Ptr")
                fnPtr := NumGet(p + off + 8, "Ptr")
                if AhkMagic._RemoteStrValid(secs, namePtr)
                    and AhkMagic._RemotePtrInText(secs, fnPtr) {
                    name := AhkMagic._RemoteReadUtf16(secs, namePtr, 64)
                    if name = anchor {
                        count := 1
                        prevName := name
                        q := off + stride
                        while q + 16 <= sec["data"].Size {
                            qNamePtr := NumGet(p + q, "Ptr")
                            qFnPtr := NumGet(p + q + 8, "Ptr")
                            if !AhkMagic._RemoteStrValid(secs, qNamePtr)
                                or !AhkMagic._RemotePtrInText(secs, qFnPtr)
                                break
                            qName := AhkMagic._RemoteReadUtf16(secs, qNamePtr, 64)
                            if StrCompare(qName, prevName, false) < 0
                                break
                            count += 1
                            prevName := qName
                            q += stride
                        }
                        if count > bestCount {
                            bestCount := count
                            bestStart := sec["rva"] + off
                        }
                    }
                }
                off += 8
            }
        }
        empty := Map("found", false, "stride", stride, "count", 0
            , "entries", Map(), "names", [])
        if bestCount < 10
            return empty
        sec := 0
        for _, s in secs
            if bestStart >= s["rva"] and bestStart < s["rva"] + s["size"] {
                sec := s
                break
            }
        if !sec or sec["data"].Size = 0
            return empty
        entries := Map()
        names := []
        off := bestStart - sec["rva"]
        loop bestCount {
            p := sec["data"].Ptr + off
            namePtr := NumGet(p, "Ptr")
            name := AhkMagic._RemoteReadUtf16(secs, namePtr, 64)
            if kind = "bif" {
                entries[name] := Map(
                    "rva", NumGet(p + 8, "Ptr") - base,
                    "min", NumGet(p + 16, "UChar"),
                    "max", NumGet(p + 17, "UChar"),
                    "fid", NumGet(p + 18, "UChar"))
            } else if kind = "mdfunc" {
                entries[name] := Map(
                    "rva", NumGet(p + 8, "Ptr") - base,
                    "ret", NumGet(p + 16, "UChar"))
            } else {
                getter := NumGet(p + 8, "Ptr")
                setter := NumGet(p + 16, "Ptr")
                entries[name] := Map(
                    "getter_rva", getter - base,
                    "setter_rva", setter ? setter - base : 0)
            }
            names.Push(name)
            off += stride
        }
        return Map("found", true, "table_rva", bestStart, "stride", stride
            , "count", bestCount, "entries", entries, "names", names)
    }

    static _RemoteLocateInternal(secs, base) {
        text := AhkMagic._TextSection(secs)
        if !text["ptr"]
            throw Error("remote text section not loaded", -1)
        postfixRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfix2 := AhkMagic._BestStart(text, postfixRefs)
        expandRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expand := AhkMagic._BestStart(text, expandRefs)
        if !postfix2 or !expand
            throw Error("remote expression functions not found", -1)
        return Map("postfix_rva", postfix2, "expand_rva", expand)
    }

    static _RemoteCurrLineSlot(secs, base, getterRva) {
        text := AhkMagic._TextSection(secs)
        p := text["ptr"]
        off := getterRva - text["rva"]
        if NumGet(p + off, "UChar") != 0x48
            or NumGet(p + off + 1, "UChar") != 0x8B
            or NumGet(p + off + 2, "UChar") != 0x05
            throw Error("unexpected remote A_LineNumber getter code", -1)
        disp := NumGet(p + off + 3, "Int")
        return base + getterRva + 7 + disp
    }

    static _HexBuffer(hex) {
        size := StrLen(hex) // 2
        buf := Buffer(size)
        loop size {
            byte := Integer("0x" SubStr(hex, 2 * A_Index - 1, 2))
            NumPut("UChar", byte, buf, A_Index - 1)
        }
        return buf
    }

    static _RemoteWrite(h, addr, buf) {
        written := 0
        if !DllCall("WriteProcessMemory", "Ptr", h, "Ptr", addr
            , "Ptr", buf.Ptr, "UPtr", buf.Size, "UPtr*", &written)
            throw Error("WriteProcessMemory failed", -1)
        if written != buf.Size
            throw Error("WriteProcessMemory short write at 0x"
                . Format("{:X}", addr) ": " written "/" buf.Size, -1)
    }

    static _RemoteReadString(h, addr, maxLen := 4096) {
        out := Buffer(0)
        chunk := 256
        loop {
            if out.Size >= maxLen
                break
            try
                part := AhkMagic._RemoteRead(h, addr + out.Size, chunk)
            catch
                break
            if part.Size = 0
                break
            combined := Buffer(out.Size + part.Size)
            if out.Size
                DllCall("RtlMoveMemory", "Ptr", combined.Ptr, "Ptr", out.Ptr
                    , "UPtr", out.Size)
            DllCall("RtlMoveMemory", "Ptr", combined.Ptr + out.Size
                , "Ptr", part.Ptr, "UPtr", part.Size)
            out := combined
            found := -1
            loop out.Size // 2 {
                if NumGet(out, (A_Index - 1) * 2, "UShort") = 0 {
                    found := A_Index - 1
                    break
                }
            }
            if found >= 0
                return StrGet(out.Ptr, found, "UTF-16")
        }
        return StrGet(out.Ptr, "UTF-16")
    }

    static AttachRemote(pid) {
        if !(pid is Integer)
            throw TypeError("pid must be an integer", -1)
        h := AhkMagic._RemoteOpen(pid, false)
        try {
            mod := AhkMagic._RemoteModuleBase(h, pid)
            secs := AhkMagic._RemoteSections(h, mod["base"])
            bif := AhkMagic._RemoteTable(h, secs, mod["base"], "bif")
            mdfunc := AhkMagic._RemoteTable(h, secs, mod["base"], "mdfunc")
            biv := AhkMagic._RemoteTable(h, secs, mod["base"], "biv")
            internal := Map()
            try {
                loc := AhkMagic._RemoteLocateInternal(secs, mod["base"])
                textSec := AhkMagic._TextSection(secs)
                loc["text_rva"] := textSec["rva"]
                loc["text_size"] := textSec["size"]
                if biv["found"] and biv["entries"].Has("LineNumber") {
                    loc["curr_line_slot"] := AhkMagic._RemoteCurrLineSlot(
                        secs, mod["base"]
                        , biv["entries"]["LineNumber"]["getter_rva"])
                    internal := loc
                }
            } catch as e {
                internal := Map("error", e.What " | " e.Message)
            }
            return Map(
                "pid", pid,
                "module", mod["path"],
                "image_base", mod["base"],
                "builtins", bif,
                "native_functions", mdfunc,
                "builtin_vars", biv,
                "internal", internal
            )
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static AttachRemoteByName(name) {
        if !(name is String)
            throw TypeError("name must be a string", -1)
        pid := AhkMagic._RemotePidByName(name)
        return AhkMagic.AttachRemote(pid)
    }

    static RemoteRedirect(hook, name, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        bif := hook["builtins"]
        if !bif["found"] or !bif["entries"].Has(name)
            or !bif["entries"].Has(newName)
            throw Error("builtin redirect names not found", -1)
        idx := 0
        for n in bif["names"] {
            if n = name
                break
            idx += 1
        }
        if idx >= bif["names"].Length
            throw Error("builtin index not found", -1)
        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            fnSlot := hook["image_base"] + bif["table_rva"]
                + idx * bif["stride"] + 8
            newPtr := hook["image_base"] + bif["entries"][newName]["rva"]
            data := Buffer(8)
            NumPut("Ptr", newPtr, data, 0)
            written := 0
            if !DllCall("WriteProcessMemory", "Ptr", h, "Ptr", fnSlot
                , "Ptr", data.Ptr, "UPtr", 8, "UPtr*", &written)
                throw Error("WriteProcessMemory failed", -1)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
        return Map("src", name, "dst", newName, "fn_slot", fnSlot)
    }

    static RemoteDeepRedirect(hook, name, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        bif := hook["builtins"]
        if !bif["found"] or !bif["entries"].Has(name)
            or !bif["entries"].Has(newName)
            throw Error("builtin redirect names not found", -1)
        idx := 0
        for n in bif["names"] {
            if n = name
                break
            idx += 1
        }
        if idx >= bif["names"].Length
            throw Error("builtin index not found", -1)

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            slot := hook["image_base"] + bif["table_rva"] + idx * bif["stride"]
            slotBuf := AhkMagic._RemoteRead(h, slot, 16)
            namePtr := NumGet(slotBuf, 0, "Ptr")
            oldFn := NumGet(slotBuf, 8, "Ptr")
            newFn := hook["image_base"] + bif["entries"][newName]["rva"]

            ; The vtable check below needs the module image bounds; read
            ; SizeOfImage from the target's PE header once.  It sits at
            ; optional-header offset 56 (i.e. 24 + 56 into the PE header).
            dos := AhkMagic._RemoteRead(h, hook["image_base"], 0x40)
            peOff := NumGet(dos, 0x3C, "UInt")
            pe := AhkMagic._RemoteRead(h, hook["image_base"] + peOff, 0x80)
            imageSize := NumGet(pe, 80, "UInt")
            imageEnd := hook["image_base"] + imageSize

            patches := []
            addr := 0
            mbi := Buffer(48)
            loop {
                if !DllCall("VirtualQueryEx", "Ptr", h, "Ptr", addr
                    , "Ptr", mbi.Ptr, "UPtr", 48)
                    break
                state := NumGet(mbi, 32, "UInt")
                protect := NumGet(mbi, 36, "UInt")
                memType := NumGet(mbi, 40, "UInt")
                regionSize := NumGet(mbi, 24, "Int64")
                baseAddr := NumGet(mbi, 0, "Ptr")
                if state = 0x1000 and memType = 0x20000
                    and (protect & 0x4 or protect & 0x40)
                    and regionSize > 0 and regionSize < 0x8000000 {
                    chunkSize := 0x10000
                    off := 0
                    while off < regionSize {
                        want := Min(chunkSize, regionSize - off)
                        try
                            data := AhkMagic._RemoteRead(h, baseAddr + off, want)
                        catch {
                            off += want
                            continue
                        }
                        maxOff := data.Size - 8
                        pos := 0
                        while pos <= maxOff {
                            if NumGet(data, pos, "Ptr") = oldFn {
                                ok := false
                                start := Max(0, pos - 0x200)
                                end := Min(data.Size - 8, pos + 0x200)
                                j := start
                                while j <= end {
                                    if NumGet(data, j, "Ptr") = namePtr {
                                        ok := true
                                        break
                                    }
                                    j += 8
                                }
                                ; The name window alone can false-positive on
                                ; unrelated structs that happen to hold both
                                ; pointers.  A real Func object matches one
                                ; of two version layouts: 2.0 puts the
                                ; vtable pointer right before the function
                                ; pointer; 2.1 packs mMinParams/mMaxParams
                                ; into the qword before it.
                                if ok {
                                    vtOk := false
                                    for bc in [pos - 0x10, pos - 0x08] {
                                        if bc < 0 or bc + 8 > data.Size
                                            continue
                                        vt := NumGet(data, bc, "Ptr")
                                        if vt >= hook["image_base"]
                                            and vt < imageEnd {
                                            vtOk := true
                                            break
                                        }
                                    }
                                    if !vtOk and pos >= 0x10 {
                                        minP := bif["entries"][name]["min"]
                                        maxP := bif["entries"][name]["max"]
                                        packed := (minP << 32) | maxP
                                        if NumGet(data, pos - 0x10, "UInt64")
                                            = packed
                                            vtOk := true
                                    }
                                    if vtOk
                                        patches.Push(baseAddr + off + pos)
                                }
                            }
                            pos += 8
                        }
                        off += want
                    }
                }
                if regionSize <= 0
                    break
                nextAddr := baseAddr + regionSize
                if nextAddr <= addr
                    break
                addr := nextAddr
            }
            if !patches.Length
                throw Error("no Func object found for " name, -1)
            data := Buffer(8)
            NumPut("Ptr", newFn, data, 0)
            ; Snapshot every slot before writing so a mid-loop failure can
            ; roll the already-patched entries back instead of leaving the
            ; target half-redirected.
            originals := []
            for p in patches
                originals.Push(AhkMagic._RemoteRead(h, p, 8))
            written := []
            try {
                loop patches.Length {
                    AhkMagic._RemoteWrite(h, patches[A_Index], data)
                    written.Push(A_Index)
                }
            } catch as e {
                for idx in written {
                    try
                        AhkMagic._RemoteWrite(h, patches[idx], originals[idx])
                    catch
                        continue
                }
                throw e
            }
            return Map("src", name, "dst", newName, "count", patches.Length)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static RemoteEval(hook, expr) {
        if !(hook is Map) or !hook.Has("pid") or !hook.Has("internal")
            or !hook["internal"].Has("postfix_rva")
            throw Error("hook has no remote eval context", -1)
        if !hook["internal"].Has("curr_line_slot")
            or !hook["internal"]["curr_line_slot"]
            throw Error("hook has no current-line slot; attach again", -1)
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)

        internal := hook["internal"]
        stub := AhkMagic._HexBuffer(MC_REMOTE_EVAL_STUB_X64)
        locator := AhkMagic._HexBuffer(MC_INTERNAL_LOCATOR_X64)
        evalBlob := AhkMagic._HexBuffer(MC_INPROC_EVAL_X64)
        codeSize := stub.Size + locator.Size + evalBlob.Size
        paramOff := (codeSize + 15) // 16 * 16
        layoutOff := paramOff + 136
        locOff := ((layoutOff + 21 * 8 + 15) // 16) * 16
        outOff := locOff + 64
        exprLen := (StrLen(expr) + 1) * 2
        exprOff := outOff + 512
        scratchOff := exprOff + exprLen
        total := scratchOff + 8 * 1024 * 1024

        ; Prefer the discovered target layout; fall back to the validated
        ; defaults when the hook has no layout yet (the blob aborts with a
        ; clear status before its first write if the assumption is wrong).
        layoutBuf := 0
        if hook.Has("script_layout") and hook["script_layout"].Has("struct")
            and hook["script_layout"].Has("shape") {
            layoutBuf := AhkMagic._BuildEvalLayout(
                hook["script_layout"]["struct"]
                , hook["script_layout"]["shape"])
        } else {
            layoutBuf := AhkMagic._DefaultEvalLayout()
        }

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
                , "UPtr", total, "UInt", 0x3000, "UInt", 0x40, "Ptr")
            if !block
                throw Error("VirtualAllocEx failed", -1)
            threadExited := false
            try {
                AhkMagic._RemoteWrite(h, block, stub)
                AhkMagic._RemoteWrite(h, block + stub.Size, locator)
                AhkMagic._RemoteWrite(h, block + stub.Size + locator.Size, evalBlob)
                AhkMagic._RemoteWrite(h, block + layoutOff, layoutBuf)

                param := Buffer(136)
                NumPut("Ptr", block + stub.Size, param, 0)
                NumPut("Ptr", block + stub.Size + locator.Size, param, 8)
                NumPut("Ptr", hook["image_base"], param, 16)
                NumPut("UInt64", internal["text_rva"], param, 24)
                NumPut("UInt64", internal["text_size"], param, 32)
                NumPut("UInt64", internal["postfix_rva"], param, 40)
                NumPut("UInt64", internal["expand_rva"], param, 48)
                NumPut("Ptr", block + locOff, param, 56)
                NumPut("Ptr", hook["image_base"] + internal["postfix_rva"], param, 64)
                NumPut("Ptr", hook["image_base"] + internal["expand_rva"], param, 72)
                NumPut("Ptr", internal["curr_line_slot"], param, 80)
                NumPut("Ptr", block + scratchOff, param, 88)
                NumPut("Ptr", block + exprOff, param, 96)
                NumPut("Ptr", block + outOff, param, 104)
                NumPut("UInt64", 0, param, 112)
                NumPut("Int", 0, param, 120)
                NumPut("Int", 0, param, 124)
                NumPut("Ptr", block + layoutOff, param, 128)
                AhkMagic._RemoteWrite(h, block + paramOff, param)

                exprBuf := Buffer(exprLen)
                StrPut(expr, exprBuf, "UTF-16")
                AhkMagic._RemoteWrite(h, block + exprOff, exprBuf)

                tid := 0
                thread := DllCall("CreateRemoteThread", "Ptr", h, "Ptr", 0
                    , "UPtr", 0, "Ptr", block, "Ptr", block + paramOff
                    , "UInt", 0, "UInt*", &tid, "Ptr")
                if !thread
                    throw Error("CreateRemoteThread failed", -1)
                try {
                    wait := DllCall("WaitForSingleObject", "Ptr", thread
                        , "UInt", 10000)
                    if wait = 0x102 {
                        ; The remote thread is still executing the injected
                        ; block.  Freeing it would leave the target running
                        ; freed memory; leak the block and fail loudly.
                        throw Error("remote eval timed out after 10s; "
                            . "injected block leaked to protect the target", -1)
                    }
                    if wait = 0xFFFFFFFF
                        throw Error("WaitForSingleObject failed for remote eval", -1)
                    if wait != 0
                        throw Error("WaitForSingleObject returned 0x"
                            . Format("{:X}", wait) " for remote eval", -1)
                    threadExited := true
                    paramBack := AhkMagic._RemoteRead(h, block + paramOff, 136)
                    locRc := NumGet(paramBack, 120, "Int")
                    evalRc := NumGet(paramBack, 124, "Int")
                    if locRc != 0
                        throw Error("remote internal locator rc=" locRc, -1)
                    if evalRc != 0
                        throw Error("remote eval rc=" evalRc, -1)
                    out := AhkMagic._RemoteRead(h, block + outOff, 512)
                    status := NumGet(out, 0, "UInt")
                    type := NumGet(out, 4, "UInt")
                    if status != 0
                        throw Error("remote eval status=" status, -1)
                    if type = 1
                        return NumGet(out, 8, "Int64")
                    if type = 2
                        return NumGet(out, 8, "Double")
                    if type = 0 {
                        ptr := NumGet(out, 16, "Ptr")
                        return ptr ? AhkMagic._RemoteReadString(h, ptr) : ""
                    }
                    if type = 5 {
                        ; Objects cannot cross process boundaries; return the
                        ; object's address in the target so callers can use
                        ; it with the remote primitives.
                        ptr := NumGet(out, 8, "Ptr")
                        if !ptr
                            throw Error("remote eval returned a null object", -1)
                        return ptr
                    }
                    throw Error("remote eval returned unknown type " type, -1)
                } finally {
                    DllCall("CloseHandle", "Ptr", thread)
                }
            } finally {
                ; Only free the block once the remote thread has exited.
                ; On timeout the block is deliberately leaked (see above).
                if threadExited
                    DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                        , "UInt", 0x8000)
            }
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteCall(h, fnAddr, args) {
        stub := AhkMagic._HexBuffer(MC_REMOTE_CALL_STUB_X64)
        param := Buffer(72)
        NumPut("Ptr", fnAddr, param, 0)
        loop 6 {
            val := args.Has(A_Index) ? args[A_Index] : 0
            NumPut("Ptr", val, param, 8 + 8 * (A_Index - 1))
        }
        NumPut("Int", 0, param, 56)
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", stub.Size + 72, "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        threadExited := false
        try {
            AhkMagic._RemoteWrite(h, block, stub)
            AhkMagic._RemoteWrite(h, block + stub.Size, param)
            tid := 0
            thread := DllCall("CreateRemoteThread", "Ptr", h, "Ptr", 0
                , "UPtr", 0, "Ptr", block, "Ptr", block + stub.Size
                , "UInt", 0, "UInt*", &tid, "Ptr")
            if !thread
                throw Error("CreateRemoteThread failed", -1)
            try {
                wait := DllCall("WaitForSingleObject", "Ptr", thread
                    , "UInt", 10000)
                if wait = 0x102 {
                    ; Thread still executing the injected block; freeing it
                    ; would corrupt the target, so leak it and fail loudly.
                    throw Error("remote call timed out after 10s; "
                        . "injected block leaked to protect the target", -1)
                }
                if wait = 0xFFFFFFFF
                    throw Error("WaitForSingleObject failed for remote call", -1)
                if wait != 0
                    throw Error("WaitForSingleObject returned 0x"
                        . Format("{:X}", wait) " for remote call", -1)
                threadExited := true
                back := AhkMagic._RemoteRead(h, block + stub.Size, 72)
                return NumGet(back, 56, "Int")
            } finally {
                DllCall("CloseHandle", "Ptr", thread)
            }
        } finally {
            if threadExited
                DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                    , "UInt", 0x8000)
        }
    }

    static _RemoteInternalLocator(h, secs, base, postfixRva, expandRva) {
        text := AhkMagic._TextSection(secs)
        locator := AhkMagic._HexBuffer(MC_INTERNAL_LOCATOR_X64)
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", locator.Size + 64, "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        try {
            AhkMagic._RemoteWrite(h, block, locator)
            locOut := block + locator.Size
            rc := AhkMagic._RemoteCall(h, block
                , [base, text["rva"], text["size"], postfixRva, expandRva, locOut])
            if rc != 0
                throw Error("remote internal locator rc=" rc, -1)
            return AhkMagic._RemoteRead(h, locOut, 64)
        } finally {
            DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                , "UInt", 0x8000)
        }
    }

    static _RemoteLoadScript(h, base, loc, text) {
        memScript := AhkMagic._HexBuffer(MC_MEM_SCRIPT_X64)
        textLen := (StrLen(text) + 1) * 2
        scratchSize := 0x400
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", memScript.Size + textLen + scratchSize
            , "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        try {
            AhkMagic._RemoteWrite(h, block, memScript)
            textAddr := block + memScript.Size
            scratchAddr := textAddr + textLen
            textBuf := Buffer(textLen)
            StrPut(text, textBuf, "UTF-16")
            AhkMagic._RemoteWrite(h, textAddr, textBuf)
            gscript := NumGet(loc["loc_out"], 0, "Ptr")
            loadTs := base + loc["load_ts_rva"]
            srcCount := loc.Has("src_count_rva") and loc["src_count_rva"]
                ? base + loc["src_count_rva"] : 0
            return AhkMagic._RemoteCall(h, block
                , [loadTs, gscript, srcCount, textAddr, StrLen(text) * 2
                    , scratchAddr])
        } finally {
            DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                , "UInt", 0x8000)
        }
    }

    static _RemoteCurrOff(secs, preparseRva) {
        text := AhkMagic._TextSection(secs)
        callers := AhkMagic._FindCallers(text, preparseRva)
        for caller in callers {
            p := text["ptr"]
            base := text["rva"]
            off := AhkMagic._FnStart(text, caller) - base
            limit := Min(text["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        if NumGet(p + j, "UChar") = 0x48
                            and NumGet(p + j + 1, "UChar") = 0x89
                            and (NumGet(p + j + 2, "UChar") = 0x58
                            or NumGet(p + j + 2, "UChar") = 0x50)
                            return NumGet(p + j + 3, "UChar")
                        j += 1
                    }
                }
                i += 1
            }
        }
        return 0
    }

    static _RemoteParserAnchors(text, loadTsRva) {
        p := text["ptr"]
        base := text["rva"]
        off := loadTsRva - base
        limit := Min(text["size"] - 8, off + 0x100)
        qCmp := -1
        dCmp := -1
        i := off
        while i < limit {
            b0 := NumGet(p + i, "UChar")
            if b0 = 0x48 and NumGet(p + i + 1, "UChar") = 0x83 {
                modrm := NumGet(p + i + 2, "UChar")
                if modrm = 0x79 and NumGet(p + i + 4, "UChar") = 0
                    and qCmp < 0
                    qCmp := NumGet(p + i + 3, "UChar")
                else if modrm = 0xB9 and NumGet(p + i + 7, "UChar") = 0
                    and qCmp < 0
                    qCmp := NumGet(p + i + 3, "Int")
            }
            if b0 = 0x83 and NumGet(p + i + 1, "UChar") = 0xB9
                and (i = off or NumGet(p + i - 1, "UChar") != 0x48)
                and NumGet(p + i + 6, "UChar") = 0
                and dCmp < 0
                dCmp := NumGet(p + i + 2, "Int")
            if qCmp >= 0 and dCmp >= 0
                break
            i += 1
        }
        if qCmp < 0 or dCmp < 0
            throw Error("parser state anchors not found", -1)
        return Map("q", qCmp, "d", dCmp)
    }

    ; Build BOTH known parser-region candidate layouts and pick the one
    ; that validates against the live target state.  No version family is
    ; assumed: each candidate must satisfy its own structural checks
    ; (sentinel value, byte-field plausibility, pointer ranges) and the
    ; winner is the candidate that strictly validates.  Ambiguous or
    ; failing candidates raise an explicit error.
    static _RemoteDiscoverParser(h, gscript, qCmp, dCmp) {
        buf := AhkMagic._RemoteRead(h, gscript, 0x800)
        candidates := []
        scores := []

        ; Candidate A: sentinel-anchored region ending at mClassObjectCount.
        if dCmp > 0 and dCmp + 4 <= buf.Size
            and NumGet(buf, dCmp, "Int") >= 0
            and NumGet(buf, dCmp, "Int") <= 100000 {
            exprIndexOff := 0
            loop Min(0x40, dCmp) // 4 {
                off := dCmp - (A_Index - 1) * 4
                if off + 8 > buf.Size
                    continue
                if NumGet(buf, off, "Int") = 0x7fffffff
                    and NumGet(buf, off + 4, "Int") = 0 {
                    exprIndexOff := off + 4
                    break
                }
            }
            if exprIndexOff and exprIndexOff + 8 <= buf.Size
                candidates.Push(Map(
                    "rich", true,
                    "fields", Map(
                        "mclass_count", dCmp,
                        "mline_parent", exprIndexOff - 0x28,
                        "mpending_related", exprIndexOff - 0x20,
                        "mlast_param_init", exprIndexOff - 0x18,
                        "mpending_hotkey", exprIndexOff - 0x10,
                        "mexpr_func", exprIndexOff - 8,
                        "mexpr_func_index", exprIndexOff,
                        "mnext_func_body", exprIndexOff + 4,
                        "mignore_block", exprIndexOff + 5,
                        "mbackcompat", exprIndexOff + 6,
                        "mcurrent_module", exprIndexOff - 0x48,
                        "mlast_module", exprIndexOff - 0x40)))
        }

        ; Candidate B: qCmp-anchored region.
        if qCmp > 0 and qCmp + 0x28 <= buf.Size
            candidates.Push(Map(
                "fields", Map(
                    "mopen", qCmp,
                    "mpending_parent", qCmp + 8,
                    "mpending_related", qCmp + 16,
                    "mlast_param_init", qCmp + 24,
                    "mnext_func_body", qCmp + 32,
                    "mclass_count", dCmp)))

        best := 0
        bestScore := -1
        for cand in candidates {
            fields := cand["fields"]
            valid := true
            for key, off in fields {
                ; Value-level checks are deliberately absent: the parser
                ; fields are saved and restored verbatim, so any plausible
                ; offset round-trips harmlessly.  The only hard requirement
                ; is that every offset stays inside the Script object.
                if !(off is Integer) or off <= 0 or off >= 0x800
                    or off + 8 > buf.Size {
                    valid := false
                    break
                }
            }
            if valid {
                ; The sentinel-anchored candidate has the richer field set
                ; and only exists when its anchor was actually found.
                score := cand.Has("rich") ? 10 : 0
                if score > bestScore {
                    bestScore := score
                    best := cand
                }
            }
        }
        if !best
            throw Error("parser state layout candidates all failed validation"
                , -1)
        return best["fields"]
    }

    static _RemoteLocateEvalScript(secs, base) {
        text := AhkMagic._TextSection(secs)
        postfixRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfixRva := AhkMagic._BestStart(text, postfixRefs)
        expandRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expandRva := AhkMagic._BestStart(text, expandRefs)
        if !postfixRva or !expandRva
            throw Error("remote expression functions not found", -1)
        callers := AhkMagic._FindCallers(text, postfixRva)
        if !callers.Length
            throw Error("PreparseExpressions not found", -1)
        preparse := AhkMagic._FnStart(text, callers[1])
        preprocess := AhkMagic._LocatePreprocessFunc(text)
        if !preprocess
            throw Error("PreprocessLocalVars not found", -1)
        open := AhkMagic._LocateOpenInclude(secs)
        if !open
            throw Error("OpenIncludedFile not found", -1)
        loadTs := AhkMagic._LocateLoadTs(text, open)
        if !loadTs
            throw Error("LoadIncludedFile(TextStream) not found", -1)
        gptr := AhkMagic._LocateGptr(text, preparse)
        if !gptr
            throw Error("g pointer not found", -1)
        currOff := AhkMagic._RemoteCurrOff(secs, preparse)
        if !currOff
            throw Error("g->curr offset not found", -1)
        anchors := AhkMagic._RemoteParserAnchors(text, loadTs)
        return Map(
            "postfix_rva", postfixRva,
            "expand_rva", expandRva,
            "preparse_rva", preparse,
            "preprocess_rva", preprocess,
            "open_rva", open,
            "load_ts_rva", loadTs,
            "src_count_rva", 0,
            "gptr_rva", gptr,
            "curr_off", currOff,
            "q_cmp", anchors["q"],
            "d_cmp", anchors["d"],
            "parser", Map())
    }

    static _RemoteLineRows(h, funcs, funcData, jumpOff, backOff) {
        rows := []
        for index, q in funcs {
            linePtr := NumGet(funcData[index], jumpOff, "Ptr")
            if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                continue
            try
                lineData := AhkMagic._RemoteRead(h, linePtr, 0x100)
            catch
                continue
            if backOff + 8 <= lineData.Size
                and NumGet(lineData, backOff, "Ptr") = q
                rows.Push(Map("func", q, "ptr", linePtr, "data", lineData))
        }
        return rows
    }

    static _RemoteLineHasFuncRef(lineData, q) {
        loop lineData.Size // 8 {
            if NumGet(lineData, (A_Index - 1) * 8, "Ptr") = q
                return true
        }
        return false
    }

    static _RemoteIsArgLike(h, p) {
        try
            argData := AhkMagic._RemoteRead(h, p, 40)
        catch
            return false
        if NumGet(argData, 0, "UChar") > 2
            or NumGet(argData, 1, "UChar") > 1
            return false
        if NumGet(argData, 4, "UInt") > 0x10000
            return false
        for off in [8, 16, 24] {
            v := NumGet(argData, off, "Ptr")
            if v and (v <= 0x10000 or v >= 0x7fffffffffff)
                return false
        }
        return true
    }

    static _RemoteTokenValid(data, stride, symOff, sentinel) {
        if symOff + 4 > stride
            return 0
        count := Min(data.Size // stride, 64)
        seenVar := false
        loop count {
            sym := NumGet(data, (A_Index - 1) * stride + symOff, "UInt")
            if sym = sentinel
                return seenVar ? A_Index : 0
            if sym > 0x1000
                return 0
            if sym = 4
                seenVar := true
        }
        return 0
    }

    static _RemoteCollectPostfixes(h, rows) {
        postfixes := []
        seen := Map()
        for row in rows {
            lineData := row["data"]
            loop lineData.Size // 8 {
                p := NumGet(lineData, (A_Index - 1) * 8, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                if !AhkMagic._RemoteIsArgLike(h, p)
                    continue
                try
                    argData := AhkMagic._RemoteRead(h, p, 40)
                catch
                    continue
                loop argData.Size // 8 {
                    q := NumGet(argData, (A_Index - 1) * 8, "Ptr")
                    if q <= 0x10000 or q >= 0x7fffffffffff
                        continue
                    if seen.Has(q)
                        continue
                    seen[q] := true
                    postfixes.Push(q)
                }
            }
        }
        return postfixes
    }

    static _RemoteDiscoverTokens(h, postfixes, sentinel) {
        bestStride := 0
        bestSym := 0
        bestScore := -1
        for stride in [24, 32, 16, 40] {
            for symOff in [16, 20, 12, 8, 4, 0] {
                if symOff + 4 > stride
                    continue
                ok := 0
                bestLen := 0
                for p in postfixes {
                    try
                        data := AhkMagic._RemoteRead(h, p, stride * 64)
                    catch
                        continue
                    got := AhkMagic._RemoteTokenValid(data, stride, symOff
                        , sentinel)
                    if got {
                        ok += 1
                        bestLen := Max(bestLen, got)
                    }
                }
                score := ok * 10000 - bestLen - stride
                if score > bestScore {
                    bestScore := score
                    bestStride := stride
                    bestSym := symOff
                }
            }
        }
        if !bestStride
            throw Error("dynamic token layout not found", -1)

        varSym := 4
        usageOff := 8
        usageScore := -1
        for uOff in [8, 12, 4, 20] {
            if uOff = bestSym
                continue
            if uOff + 8 > bestStride
                continue
            score := 0
            for p in postfixes {
                try
                    data := AhkMagic._RemoteRead(h, p, bestStride * 64)
                catch
                    continue
                loop data.Size // bestStride {
                    off := (A_Index - 1) * bestStride
                    if NumGet(data, off + bestSym, "UInt") = varSym
                        and NumGet(data, off + uOff, "UInt64") < 0x1000
                        score += 1
                }
            }
            if score > usageScore {
                usageScore := score
                usageOff := uOff
            }
        }

        valueOff := 0
        valueScore := -1
        for vOff in [0, 8, 16] {
            if vOff = bestSym
                continue
            score := 0
            for p in postfixes {
                try
                    data := AhkMagic._RemoteRead(h, p, bestStride * 64)
                catch
                    continue
                loop data.Size // bestStride {
                    off := (A_Index - 1) * bestStride
                    if NumGet(data, off + bestSym, "UInt") = varSym {
                        v := NumGet(data, off + vOff, "Ptr")
                        if v > 0x10000 and v < 0x7fffffffffff
                            score += 1
                    }
                }
            }
            if score > valueScore {
                valueScore := score
                valueOff := vOff
            }
        }

        return Map(
            "stride", bestStride,
            "symbol", bestSym,
            "usage", usageOff,
            "value", valueOff,
            "var_symbol", varSym)
    }

    static _RemoteLineHeaderOff(rows) {
        bestAction := 0
        bestArgc := 1
        bestScore := -1
        loop 4 {
            ao := A_Index - 1
            loop 4 {
                ac := A_Index - 1
                if ac = ao
                    continue
                score := 0
                for row in rows {
                    data := row["data"]
                    if ao + 1 >= data.Size or ac + 1 >= data.Size
                        continue
                    if NumGet(data, ao, "UChar") <= 0x40
                        and NumGet(data, ac, "UChar") <= 0x10
                        score += 1
                }
                if score > bestScore {
                    bestScore := score
                    bestAction := ao
                    bestArgc := ac
                }
            }
        }
        if !bestScore
            throw Error("dynamic Line header layout not found", -1)
        return Map("action", bestAction, "argc", bestArgc)
    }

    static _RemoteLineArgOff(h, rows, tokens, sentinel) {
        counts := Map()
        argPostfixCounts := Map()
        for row in rows {
            lineData := row["data"]
            loop lineData.Size // 8 {
                lineOff := (A_Index - 1) * 8
                p := NumGet(lineData, lineOff, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                if !AhkMagic._RemoteIsArgLike(h, p)
                    continue
                try
                    argData := AhkMagic._RemoteRead(h, p, 40)
                catch
                    continue
                loop argData.Size // 8 {
                    argOff := (A_Index - 1) * 8
                    q := NumGet(argData, argOff, "Ptr")
                    if q <= 0x10000 or q >= 0x7fffffffffff
                        continue
                    try
                        tokenData := AhkMagic._RemoteRead(h, q
                            , tokens["stride"] * 64)
                    catch
                        continue
                    if AhkMagic._RemoteTokenValid(tokenData
                        , tokens["stride"], tokens["symbol"], sentinel) {
                        counts[lineOff] := (counts.Has(lineOff)
                            ? counts[lineOff] : 0) + 1
                        argPostfixCounts[argOff] := (argPostfixCounts.Has(argOff)
                            ? argPostfixCounts[argOff] : 0) + 1
                    }
                }
            }
        }
        bestOff := 0
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and bestOff
                and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        bestArgPostfix := 24
        bestArgScore := 0
        for off, score in argPostfixCounts {
            if score > bestArgScore or (score = bestArgScore
                and bestArgPostfix and off < bestArgPostfix) {
                bestArgScore := score
                bestArgPostfix := off
            }
        }
        if !bestOff or !bestArgScore
            throw Error("dynamic Line.mArg / ArgStruct.postfix not found", -1)
        return Map("line_arg", bestOff, "arg_postfix", bestArgPostfix)
    }

    static _RemoteArgExpressionOff(h, rows, lineArg, argPostfix, tokens
        , sentinel) {
        counts := Map()
        for row in rows {
            arg := NumGet(row["data"], lineArg, "Ptr")
            if arg <= 0x10000 or arg >= 0x7fffffffffff
                continue
            try
                argData := AhkMagic._RemoteRead(h, arg, 40)
            catch
                continue
            postfix := NumGet(argData, argPostfix, "Ptr")
            if postfix <= 0x10000 or postfix >= 0x7fffffffffff
                continue
            try
                tokenData := AhkMagic._RemoteRead(h, postfix
                    , tokens["stride"] * 64)
            catch
                continue
            if !AhkMagic._RemoteTokenValid(tokenData, tokens["stride"]
                , tokens["symbol"], sentinel)
                continue
            loop 8 {
                off := A_Index - 1
                if NumGet(argData, off, "UChar") = 1
                    counts[off] := (counts.Has(off) ? counts[off] : 0) + 1
            }
        }
        bestOff := 1
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        if !bestScore
            throw Error("dynamic ArgStruct.is_expression not found", -1)
        return bestOff
    }

    static _RemoteLineNextOff(h, rows, lineArg, backOff) {
        forward := Map()
        total := Map()
        loop 0x100 // 8 {
            off := (A_Index - 1) * 8
            if off = lineArg
                continue
            for row in rows {
                p := NumGet(row["data"], off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    or p = row["ptr"]
                    continue
                try
                    lineData := AhkMagic._RemoteRead(h, p, 0x100)
                catch
                    continue
                if !AhkMagic._RemoteLineHasFuncRef(lineData, row["func"])
                    continue
                total[off] := (total.Has(off) ? total[off] : 0) + 1
                if p > row["ptr"]
                    forward[off] := (forward.Has(off) ? forward[off] : 0) + 1
            }
        }
        bestOff := 0
        bestForward := -1
        bestTotal := -1
        for off, score in forward {
            t := total.Has(off) ? total[off] : 0
            if score > bestForward
                or (score = bestForward and t > bestTotal)
                or (score = bestForward and t = bestTotal and bestOff
                    and off < bestOff) {
                bestForward := score
                bestTotal := t
                bestOff := off
            }
        }
        if !bestOff
            throw Error("dynamic Line.mNextLine not found", -1)
        return bestOff
    }

    static _RemoteLineAttributeOff(h, rows, lineArg, lineNext, backOff
        , funcs) {
        known := Map()
        for q in funcs
            known[q] := true
        counts := Map()
        for row in rows {
            visited := Map()
            stack := [row["ptr"]]
            while stack.Length {
                line := stack.Pop()
                if visited.Has(line)
                    continue
                visited[line] := true
                try
                    lineData := AhkMagic._RemoteRead(h, line, 0x100)
                catch
                    continue
                loop lineData.Size // 8 {
                    off := (A_Index - 1) * 8
                    if off = lineArg or off = lineNext
                        continue
                    p := NumGet(lineData, off, "Ptr")
                    if known.Has(p) {
                        counts[off] := (counts.Has(off)
                            ? counts[off] : 0) + 1
                        continue
                    }
                    if p <= 0x10000 or p >= 0x7fffffffffff or p = line
                        continue
                    if AhkMagic._RemoteLineHasFuncRef(lineData, row["func"])
                        and !visited.Has(p)
                        stack.Push(p)
                }
            }
        }
        bestOff := 0
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and bestOff
                and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        if !bestOff
            throw Error("dynamic Line.mAttribute not found", -1)
        return bestOff
    }

    static _RemoteDerefArrayLike(data) {
        if NumGet(data, 0, "Ptr") <= 0x10000
            or NumGet(data, 0, "Ptr") >= 0x7fffffffffff
            return false
        loop 24 {
            to := A_Index - 1
            if to < 8 or to + 8 > data.Size
                continue
            if NumGet(data, to, "UChar") <= 7
                and NumGet(data, to + 4, "UInt") <= 0x10000
                return true
        }
        return false
    }

    static _RemoteDerefLayout(h, rows, lineArg) {
        arrays := []
        argDerefCounts := Map()
        for row in rows {
            arg := NumGet(row["data"], lineArg, "Ptr")
            if arg <= 0x10000 or arg >= 0x7fffffffffff
                continue
            try
                argData := AhkMagic._RemoteRead(h, arg, 40)
            catch
                continue
            loop argData.Size // 8 {
                off := (A_Index - 1) * 8
                p := NumGet(argData, off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                try
                    data := AhkMagic._RemoteRead(h, p, 0x100)
                catch
                    continue
                if AhkMagic._RemoteDerefArrayLike(data) {
                    arrays.Push(data)
                    argDerefCounts[off] := (argDerefCounts.Has(off)
                        ? argDerefCounts[off] : 0) + 1
                }
            }
        }
        if arrays.Length < 1
            throw Error("dynamic ArgStruct.deref not found", -1)

        argDerefOff := 0
        argDerefScore := -1
        for off, score in argDerefCounts {
            if score > argDerefScore
                or (score = argDerefScore and off < argDerefOff) {
                argDerefScore := score
                argDerefOff := off
            }
        }
        if !argDerefScore
            throw Error("dynamic ArgStruct.deref offset not found", -1)

        bestStride := 24
        bestType := 16
        bestScore := -1
        for stride in [24, 32, 16, 40] {
            for typeOff in [16, 20, 24] {
                if typeOff + 8 > stride
                    continue
                score := 0
                for data in arrays {
                    loop Min(data.Size // stride, 8) {
                        off := (A_Index - 1) * stride
                        marker := NumGet(data, off, "Ptr")
                        if marker <= 0x10000
                            or marker >= 0x7fffffffffff
                            break
                        if NumGet(data, off + typeOff, "UChar") <= 7
                            and NumGet(data, off + typeOff + 1, "UChar") <= 3
                            and NumGet(data, off + typeOff + 4, "UInt")
                                <= 0x10000
                            score += 1
                    }
                }
                if score > bestScore
                    or (score = bestScore and typeOff < bestType)
                    or (score = bestScore and typeOff = bestType
                        and stride < bestStride) {
                    bestScore := score
                    bestType := typeOff
                    bestStride := stride
                }
            }
        }
        if !bestScore
            throw Error("dynamic DerefType layout not found", -1)

        markerOff := bestType - 16
        varOff := bestType - 8
        markerScore := 0
        for data in arrays {
            if NumGet(data, markerOff, "Ptr") > 0x10000
                and NumGet(data, markerOff, "Ptr") < 0x7fffffffffff
                markerScore += 1
        }
        if !markerScore
            throw Error("dynamic DerefType markers not found", -1)

        return Map(
            "arg_deref", argDerefOff,
            "stride", bestStride,
            "marker", markerOff,
            "var", varOff,
            "type", bestType,
            "len", bestType + 4)
    }

    static _RemoteDiscoverBackOff(h, funcs, funcData, jumpOff) {
        counts := Map()
        for index, q in funcs {
            linePtr := NumGet(funcData[index], jumpOff, "Ptr")
            if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                continue
            try
                lineData := AhkMagic._RemoteRead(h, linePtr, 0x80)
            catch
                continue
            loop lineData.Size // 8 {
                bo := (A_Index - 1) * 8
                if NumGet(lineData, bo, "Ptr") = q
                    counts[bo] := (counts.Has(bo) ? counts[bo] : 0) + 1
            }
        }
        bestOff := 0
        bestCount := 0
        for off, count in counts {
            if count > bestCount or (count = bestCount and off < bestOff) {
                bestCount := count
                bestOff := off
            }
        }
        if !bestOff
            throw Error("back-reference slot not found", -1)
        return bestOff
    }

    static _DiscoverInProcStructs(gScript, arrPtr, oldCount, newFunc
        , jumpOff) {
        h := DllCall("OpenProcess", "UInt", 0x1F0FFF, "Int", 0
            , "UInt", DllCall("GetCurrentProcessId"), "Ptr")
        if !h
            throw Error("OpenProcess(self) failed", -1)
        try {
            funcs := []
            funcData := []
            loop oldCount {
                q := NumGet(arrPtr + (A_Index - 1) * 8, "Ptr")
                if q <= 0x10000 or q >= 0x7fffffffffff
                    continue
                funcs.Push(q)
                data := Buffer(0x400)
                DllCall("RtlMoveMemory", "Ptr", data.Ptr, "Ptr", q
                    , "UPtr", 0x400)
                funcData.Push(data)
            }
            funcs.Push(newFunc)
            data := Buffer(0x400)
            DllCall("RtlMoveMemory", "Ptr", data.Ptr, "Ptr", newFunc
                , "UPtr", 0x400)
            funcData.Push(data)
            backOff := AhkMagic._RemoteDiscoverBackOff(h, funcs, funcData
                , jumpOff)
            locOut := Buffer(64, 0)
            NumPut("Ptr", AhkMagic.gScript, locOut, 0)
            NumPut("Ptr", AhkMagic.finalizeExpr, locOut, 8)
            NumPut("Ptr", AhkMagic.findOrAddVar, locOut, 16)
            NumPut("Ptr", AhkMagic.crtFree, locOut, 24)
            NumPut("UInt", AhkMagic.symInvalid, locOut, 32)
            return AhkMagic._RemoteDiscoverStructs(h, locOut, funcs
                , funcData, jumpOff, backOff)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteDiscoverStructs(h, locOut, funcs, funcData, jumpOff
        , backOff) {
        rows := AhkMagic._RemoteLineRows(h, funcs, funcData, jumpOff, backOff)
        if rows.Length < 1
            throw Error("no ordinary functions for struct discovery"
                , -1)
        sentinel := NumGet(locOut, 32, "UInt")
        postfixes := AhkMagic._RemoteCollectPostfixes(h, rows)
        tokens := AhkMagic._RemoteDiscoverTokens(h, postfixes, sentinel)
        header := AhkMagic._RemoteLineHeaderOff(rows)
        arg := AhkMagic._RemoteLineArgOff(h, rows, tokens, sentinel)
        argExpr := AhkMagic._RemoteArgExpressionOff(h, rows
            , arg["line_arg"], arg["arg_postfix"], tokens, sentinel)
        lineNext := AhkMagic._RemoteLineNextOff(h, rows, arg["line_arg"]
            , backOff)
        lineAttr := AhkMagic._RemoteLineAttributeOff(h, rows
            , arg["line_arg"], lineNext, backOff, funcs)
        deref := AhkMagic._RemoteDerefLayout(h, rows, arg["line_arg"])
        return Map(
            "line_action", header["action"],
            "line_argc", header["argc"],
            "line_arg", arg["line_arg"],
            "line_attribute", lineAttr,
            "line_next", lineNext,
            "arg_expression", argExpr,
            "arg_postfix", arg["arg_postfix"],
            "arg_deref", deref["arg_deref"],
            "token_stride", tokens["stride"],
            "token_symbol", tokens["symbol"],
            "token_usage", tokens["usage"],
            "token_value", tokens["value"],
            "token_var_symbol", tokens["var_symbol"],
            "deref_stride", deref["stride"],
            "deref_marker", deref["marker"],
            "deref_var", deref["var"],
            "deref_type", deref["type"],
            "deref_len", deref["len"])
    }

    ; Discover the mFuncs layout of a target.  The validator needs live
    ; function entries, so a target whose script defines no functions
    ; (fresh/empty scripts) fails discovery.  Inject two probe functions
    ; through the target's own loader and retry once; the probes stay in
    ; the target's mFuncs as harmless extras.
    static _RemoteDiscoverLayoutWithProbe(h, secs, base, loc) {
        try {
            return AhkMagic._RemoteDiscoverLayout(h, secs, base, loc)
        } catch as e {
            if !InStr(e.Message, "dynamic function layout not found")
                and !InStr(e.Message, "dynamic token layout not found")
                throw e
            tag := Format("{:x}", A_TickCount)
            probe := "ahkHackLayoutProbeA" tag
                . "(x) {`n    return x + 1`n}`n"
                . "ahkHackLayoutProbeB" tag
                . "(y) {`n    return y * 2`n}`n"
            rc := AhkMagic._RemoteLoadScript(h, base, loc, probe)
            if rc != 0
                throw Error("probe injection for layout discovery failed rc="
                    . rc " (TextStream layout validation failed in target)"
                    , -1)
            try {
                ; First pass discovers mFuncs/mjumpLine without structs.
                layout := AhkMagic._RemoteDiscoverLayout(
                    h, secs, base, loc, false)
                ; The injected functions were loaded without preparse, so
                ; their lines have no postfix buffers yet.  Preparse them
                ; through the target's own pipeline, then finish struct
                ; discovery on the now-complete probe rows.
                savedCur := AhkMagic._RPtr(h, layout["g"] + layout["curr_off"])
                for q in layout["funcs"] {
                    jump := AhkMagic._RPtr(h
                        , q + layout["mjump_line_off"])
                    if !jump
                        continue
                    AhkMagic._WPtr(h, layout["g"] + layout["curr_off"], q)
                    prc := AhkMagic._RemoteCall(h
                        , base + loc["preparse_rva"]
                        , [layout["gscript"], jump])
                    AhkMagic._WPtr(h, layout["g"] + layout["curr_off"], savedCur)
                    if prc != 1
                        throw Error("probe PreparseExpressions rc=" prc, -1)
                }
                layout["struct"] := AhkMagic._RemoteDiscoverStructs(
                    h, loc["loc_out"], layout["funcs"], layout["func_data"]
                    , layout["mjump_line_off"], layout["jump_back_off"])
                layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                    , layout["struct"])
                return layout
            } catch as e2 {
                throw Error("layout discovery failed even after probe "
                    . "injection: " e2.Message, -1)
            }
        }
    }

    static _RemoteDiscoverLayout(h, secs, base, loc, doStructs := true) {
        locOut := AhkMagic._RemoteInternalLocator(h, secs, base
            , loc["postfix_rva"], loc["expand_rva"])
        loc["loc_out"] := locOut
        gscript := NumGet(locOut, 0, "Ptr")
        gAddr := base + loc["gptr_rva"]
        g := AhkMagic._RPtr(h, gAddr)
        currOff := loc["curr_off"]
        snap := AhkMagic._RemoteRead(h, gscript, 0x800)
        bestScore := -1
        best := Map("funcs_off", 0, "count_off", 0, "arr_ptr", 0
            , "count", 0, "name_off", 0, "jump_off", 0
            , "back_off", 0, "funcs", [], "func_data", [])

        loop 0x200 // 8 {
            off := (A_Index - 1) * 8
            if off + 12 > snap.Size
                continue
            arrPtr := NumGet(snap, off, "Ptr")
            count := NumGet(snap, off + 8, "Int")
            if arrPtr <= 0x10000 or arrPtr >= 0x7fffffffffff
                or count < 1 or count > 100000
                continue

            limit := Min(count, 16)
            funcs := []
            funcData := []
            lineCache := Map()
            valid := true
            loop limit {
                try
                    q := AhkMagic._RPtr(h, arrPtr + (A_Index - 1) * 8)
                catch {
                    valid := false
                    break
                }
                if q <= 0x10000 or q >= 0x7fffffffffff {
                    valid := false
                    break
                }
                funcs.Push(q)
                funcData.Push(AhkMagic._RemoteRead(h, q, 0x400))
            }
            if !valid
                continue

            jumpOff := 0
            jumpMatches := 0
            jumpBestSlot := 0
            jumpSlotOff := 0
            loop 0x200 // 8 {
                jo := (A_Index - 1) * 8
                matches := 0
                slotCount := Map()
                for index, q in funcs {
                    linePtr := NumGet(funcData[index], jo, "Ptr")
                    if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                        continue
                    if lineCache.Has(linePtr) {
                        lineData := lineCache[linePtr]
                    } else {
                        try {
                            lineData := AhkMagic._RemoteRead(h, linePtr, 0x80)
                            lineCache[linePtr] := lineData
                        } catch {
                            continue
                        }
                    }
                    foundSlot := 0
                    loop lineData.Size // 8 {
                        bo := (A_Index - 1) * 8
                        if NumGet(lineData, bo, "Ptr") = q {
                            foundSlot := bo
                            break
                        }
                    }
                    if foundSlot {
                        matches += 1
                        slotCount[foundSlot] := (slotCount.Has(foundSlot)
                            ? slotCount[foundSlot] : 0) + 1
                    }
                }
                bestSlot := 0
                bestSlotCount := 0
                for slot, count in slotCount {
                    if count > bestSlotCount or count = bestSlotCount {
                        bestSlotCount := count
                        bestSlot := slot
                    }
                }
                if bestSlotCount > jumpBestSlot
                    or (bestSlotCount = jumpBestSlot and jumpBestSlot
                        and jo < jumpOff) {
                    jumpMatches := matches
                    jumpBestSlot := bestSlotCount
                    jumpSlotOff := bestSlot
                    jumpOff := jo
                }
            }

            minJump := limit < 2 ? 1 : 2
            if jumpBestSlot < minJump
                continue
            ; A real mFuncs array must have back-reference rows for a
            ; majority of its entries.  Decoy arrays (other object lists in
            ; the Script object) can contain a few function-like pointers
            ; and win the name-based score; rejecting sub-majority
            ; back-reference counts keeps the scan on the real array.
            if jumpBestSlot * 2 < limit
                continue

            nameOff := 0
            nameMatches := 0
            nameUnique := 0
            loop 0x200 // 8 {
                no := (A_Index - 1) * 8
                matches := 0
                seen := Map()
                for index, q in funcs {
                    namePtr := NumGet(funcData[index], no, "Ptr")
                    if namePtr <= 0x10000 or namePtr >= 0x7fffffffffff
                        continue
                    try
                        name := AhkMagic._RemoteReadString(h, namePtr, 128)
                    catch
                        continue
                    if name = "" or StrLen(name) > 127
                        continue
                    if !RegExMatch(name, "^[A-Za-z_][A-Za-z0-9_.]*$")
                        continue
                    matches += 1
                    seen[name] := 1
                }
                if matches > nameMatches
                    or (matches = nameMatches and seen.Count > nameUnique) {
                    nameMatches := matches
                    nameUnique := seen.Count
                    nameOff := no
                }
            }

            minName := limit < 2 ? 1 : 2
            if nameMatches < minName
                continue

            score := nameMatches * 100000 + nameUnique * 1000
                + jumpMatches * 100 + limit
            if score > bestScore {
                bestScore := score
                best["funcs_off"] := off
                best["count_off"] := off + 8
                best["arr_ptr"] := arrPtr
                best["count"] := count
                best["name_off"] := nameOff
                best["jump_off"] := jumpOff
                best["back_off"] := jumpSlotOff
                best["funcs"] := funcs
                best["func_data"] := funcData
            }
        }

        if !best["funcs_off"] or !best["name_off"] or !best["jump_off"]
            throw Error("dynamic function layout not found", -1)

        loc["parser"] := AhkMagic._RemoteDiscoverParser(h, gscript
            , loc["q_cmp"], loc["d_cmp"])
        layout := Map(
            "gscript", gscript,
            "g", g,
            "curr_off", currOff,
            "mfuncs_off", best["funcs_off"],
            "mfuncs_count_off", best["count_off"],
            "mlast_line_off", 0,
            "mjump_line_off", best["jump_off"],
            "name_off", best["name_off"],
            "jump_back_off", best["back_off"],
            "funcs", best["funcs"],
            "func_data", best["func_data"],
            "parser", loc["parser"])
        if doStructs {
            layout["struct"] := AhkMagic._RemoteDiscoverStructs(h, locOut
                , best["funcs"], best["func_data"], best["jump_off"]
                , best["back_off"])
            layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                , layout["struct"])
        }
        return layout
    }

    static _RPtr(h, addr) {
        return NumGet(AhkMagic._RemoteRead(h, addr, 8), 0, "Ptr")
    }

    static _RInt(h, addr) {
        return NumGet(AhkMagic._RemoteRead(h, addr, 4), 0, "Int")
    }

    static _WPtr(h, addr, val) {
        buf := Buffer(8)
        NumPut("Ptr", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _WInt(h, addr, val) {
        buf := Buffer(4)
        NumPut("Int", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _WByte(h, addr, val) {
        buf := Buffer(1)
        NumPut("UChar", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _DeclaredFunctionNames(text) {
        names := []
        depth := 0
        candidate := ""
        for raw in StrSplit(text, "`n", "`r") {
            line := Trim(raw)
            if candidate = "" and line != "" and SubStr(line, 1, 1) != ";" {
                if RegExMatch(line
                    , "^([A-Za-z_][A-Za-z0-9_]*)\s*\(", &m)
                    and !RegExMatch(line
                        , "^(if|else|for|while|switch|try|catch|finally"
                        . "|return|break|continue|global|local|static|class"
                        . "|new|throw|until)\b") {
                    candidate := m[1]
                }
            }
            if candidate != "" and InStr(line, "{") {
                found := false
                for n in names {
                    if n = candidate {
                        found := true
                        break
                    }
                }
                if !found and depth = 0
                    names.Push(candidate)
                candidate := ""
            }
            opens := 0
            closes := 0
            loop StrLen(line) {
                c := SubStr(line, A_Index, 1)
                if c = "{"
                    opens += 1
                else if c = "}"
                    closes += 1
            }
            depth += opens - closes
        }
        return names
    }

    static RemoteEvalScript(hook, text) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        if !(text is String) or Trim(text) = ""
            throw TypeError("text must be a non-empty string", -1)
        ; Same protection as the in-process path, but validated with the
        ; TARGET's own interpreter when its path is known.
        vpath := hook.Has("module") and hook["module"] != ""
            ? hook["module"] : A_AhkPath
        AhkMagic._ValidateScript(text, vpath)

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            mod := AhkMagic._RemoteModuleBase(h, hook["pid"])
            secs := AhkMagic._RemoteSections(h, mod["base"])
            if hook.Has("script_loc") and hook.Has("script_layout") {
                loc := hook["script_loc"]
                layout := hook["script_layout"]
            } else {
                loc := AhkMagic._RemoteLocateEvalScript(secs, mod["base"])
                layout := AhkMagic._RemoteDiscoverLayoutWithProbe(
                    h, secs, mod["base"], loc)
                hook["script_loc"] := loc
                hook["script_layout"] := layout
            }
            locOut := loc["loc_out"]
            if !layout.Has("struct")
                layout["struct"] := AhkMagic._RemoteDiscoverStructs(h, locOut
                    , layout["funcs"], layout["func_data"]
                    , layout["mjump_line_off"], layout["jump_back_off"])
            if !layout.Has("shape")
                layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                    , layout["struct"])
            s := layout["struct"]
            gscript := layout["gscript"]
            g := layout["g"]
            currOff := layout["curr_off"]
            for name in AhkMagic._DeclaredFunctionNames(text) {
                if AhkMagic._RemoteFindUserFuncs(h, layout, name).Length
                    throw Error("function name already exists in target: "
                        . name, -1)
            }
            oldFuncCount := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
            savedCur := AhkMagic._RPtr(h, g + currOff)
            savedCurFunc := AhkMagic._RPtr(h, g + currOff)

            parser := layout["parser"]
            savedState := []
            for key in ["mopen", "mpending_parent", "mline_parent"
                , "mpending_related", "mlast_param_init", "mpending_hotkey"
                , "mexpr_func", "mcurrent_module"]
                if parser.Has(key)
                    savedState.Push([key, "Ptr"
                        , AhkMagic._RPtr(h, gscript + parser[key])])
            for key in ["mexpr_func_index", "mclass_count"]
                if parser.Has(key)
                    savedState.Push([key, "Int"
                        , AhkMagic._RInt(h, gscript + parser[key])])
            for key in ["mnext_func_body", "mignore_block", "mbackcompat"]
                if parser.Has(key)
                    savedState.Push([key, "UChar"
                        , NumGet(AhkMagic._RemoteRead(h, gscript + parser[key], 1)
                            , 0, "UChar")])

            try {
                for key in ["mopen", "mpending_parent", "mline_parent"
                    , "mpending_related", "mlast_param_init"
                    , "mpending_hotkey", "mexpr_func"]
                    if parser.Has(key)
                        AhkMagic._WPtr(h, gscript + parser[key], 0)
                if parser.Has("mexpr_func_index")
                    AhkMagic._WInt(h, gscript + parser["mexpr_func_index"]
                        , 0x7fffffff)
                if parser.Has("mnext_func_body")
                    AhkMagic._WByte(h, gscript + parser["mnext_func_body"], 0)
                if parser.Has("mignore_block")
                    AhkMagic._WByte(h, gscript + parser["mignore_block"], 0)
                if parser.Has("mbackcompat")
                    AhkMagic._WByte(h, gscript + parser["mbackcompat"], 1)
                if parser.Has("mclass_count")
                    AhkMagic._WInt(h, gscript + parser["mclass_count"], 0)
                AhkMagic._WPtr(h, g + currOff, 0)

                rc := AhkMagic._RemoteLoadScript(h, mod["base"], loc, text)
                if rc != 0
                    throw Error("LoadIncludedFile(memory) rc=" rc, -1)

                funcsItem := AhkMagic._RPtr(h, gscript + layout["mfuncs_off"])
                funcCount := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
                if funcCount < oldFuncCount
                    or funcCount > oldFuncCount + 4096
                    throw Error("mFuncs count changed unexpectedly: "
                        . oldFuncCount " -> " funcCount, -1)
                if funcCount > oldFuncCount {
                    loop funcCount - oldFuncCount {
                        idx := oldFuncCount + A_Index - 1
                        newFunc := AhkMagic._RPtr(h, funcsItem + idx * 8)
                        jump := AhkMagic._RPtr(h
                            , newFunc + layout["mjump_line_off"])
                        if !jump
                            continue
                        AhkMagic._WPtr(h, g + currOff, newFunc)
                        rc := AhkMagic._RemoteCall(h
                            , mod["base"] + loc["preparse_rva"]
                            , [gscript, jump])
                        if rc != 1
                            throw Error("PreparseExpressions rc=" rc, -1)
                        AhkMagic._WPtr(h, g + currOff, newFunc)
                        line := jump
                        lineSteps := 0
                        while line {
                            if lineSteps > 100000
                                throw Error("remote line chain too long; layout likely wrong", -1)
                            lineSteps += 1
                            lineSize := Max(0x40, s["line_next"] + 8
                                , s["line_attribute"] + 8, s["line_arg"] + 8)
                            lineData := AhkMagic._RemoteRead(h, line, lineSize)
                            action := NumGet(lineData, s["line_action"], "UChar")
                            attr := NumGet(lineData, s["line_attribute"], "Ptr")
                            argc := NumGet(lineData, s["line_argc"], "UChar")
                            arg := NumGet(lineData, s["line_arg"], "Ptr")
                            if action = 3 and attr
                                AhkMagic._WPtr(h, g + currOff, attr)
                            if argc and arg
                                and NumGet(AhkMagic._RemoteRead(h
                                    , arg + s["arg_expression"], 1)
                                    , 0, "UChar") {
                                postfix := AhkMagic._RPtr(h
                                    , arg + s["arg_postfix"])
                                if postfix {
                                    tokenSteps := 0
                                    loop {
                                        if tokenSteps > 100000
                                            throw Error("remote postfix token chain too long; layout likely wrong", -1)
                                        tokenSteps += 1
                                        tokenSize := Max(s["token_stride"]
                                            , s["token_symbol"] + 4
                                            , s["token_usage"] + 8
                                            , s["token_value"] + 8)
                                        token := AhkMagic._RemoteRead(h
                                            , postfix, tokenSize)
                                        symbol := NumGet(token
                                            , s["token_symbol"], "UInt")
                                        if symbol = NumGet(locOut, 32, "UInt")
                                            break
                                        if symbol = s["token_var_symbol"]
                                            and NumGet(token, s["token_usage"]
                                                , "UInt") < 3 {
                                            deref := NumGet(token
                                                , s["token_value"], "Ptr")
                                            if deref {
                                                derefSize := Max(24
                                                    , s["deref_len"] + 4
                                                    , s["deref_type"] + 1
                                                    , s["deref_marker"] + 8
                                                    , s["deref_var"] + 8)
                                                derefData := AhkMagic._RemoteRead(h
                                                    , deref, derefSize)
                                                derefType := NumGet(derefData
                                                    , s["deref_type"], "UChar")
                                                marker := NumGet(derefData
                                                    , s["deref_marker"], "Ptr")
                                                derefLen := NumGet(derefData
                                                    , s["deref_len"], "UInt")
                                                if derefType = 7 {
                                                    AhkMagic._WPtr(h, postfix
                                                        , AhkMagic._RPtr(h
                                                            , deref
                                                            + s["deref_var"]))
                                                } else if derefType = 0
                                                    and marker
                                                    and derefLen > 0
                                                    and derefLen <= 64 {
                                                    var := AhkMagic._RemoteCall(h
                                                        , NumGet(locOut, 16, "Ptr")
                                                        , [gscript, marker
                                                            , derefLen, 0x103])
                                                    if var
                                                        AhkMagic._WPtr(h
                                                            , postfix
                                                                + s["token_value"]
                                                            , var)
                                                }
                                            }
                                        }
                                        postfix += s["token_stride"]
                                    }
                                }
                            }
                            line := AhkMagic._RPtr(h
                                , line + s["line_next"])
                        }
                        AhkMagic._WPtr(h, g + currOff, savedCur)
                        rc := AhkMagic._RemoteCall(h
                            , mod["base"] + loc["preprocess_rva"]
                            , [gscript, newFunc])
                        if rc != 1
                            throw Error("PreprocessLocalVars rc=" rc, -1)
                        AhkMagic._WPtr(h, g + currOff, savedCurFunc)
                    }
                }
                AhkMagic._WPtr(h, g + currOff, 0)

                last := ""
                for raw in StrSplit(text, "`n", "`r") {
                    t := Trim(raw)
                    if t = ""
                        continue
                    if RegExMatch(t
                        , "^(if|else|for|while|loop|try|catch|finally|return|break|continue|class|static|global|local|throw)\b")
                        continue
                    if SubStr(t, -1) = "{"
                        continue
                    last := t
                }
                if last = ""
                    throw ValueError("no expression result found in script text")
                return AhkMagic.RemoteEval(hook, last)
            } finally {
                for item in savedState {
                    if item[2] = "Ptr"
                        AhkMagic._WPtr(h, gscript + parser[item[1]], item[3])
                    else if item[2] = "Int"
                        AhkMagic._WInt(h, gscript + parser[item[1]], item[3])
                    else
                        AhkMagic._WByte(h, gscript + parser[item[1]], item[3])
                }
                AhkMagic._WPtr(h, g + currOff, savedCurFunc)
                AhkMagic._WPtr(h, g + currOff, savedCur)
            }
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static RemoteReplaceFuncBody(hook, oldName, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            mod := AhkMagic._RemoteModuleBase(h, hook["pid"])
            secs := AhkMagic._RemoteSections(h, mod["base"])
            if hook.Has("script_layout") {
                layout := hook["script_layout"]
            } else {
                loc := AhkMagic._RemoteLocateEvalScript(secs, mod["base"])
                layout := AhkMagic._RemoteDiscoverLayoutWithProbe(
                    h, secs, mod["base"], loc)
                hook["script_loc"] := loc
                hook["script_layout"] := layout
            }
            oldList := AhkMagic._RemoteFindUserFuncs(h, layout, oldName)
            newPtr := AhkMagic._RemoteFindUserFunc(h, layout, newName)
            if !oldList.Length or !newPtr
                throw Error("function object not found: " oldName " / " newName, -1)
            jumpOff := layout["mjump_line_off"]
            newJump := AhkMagic._RPtr(h, newPtr + jumpOff)
            if !newJump
                throw Error("new function body not found", -1)
            patched := []
            for oldPtr in oldList {
                AhkMagic._WPtr(h, oldPtr + jumpOff, newJump)
                patched.Push(oldPtr)
            }
            return Map("old", oldList, "new", newPtr, "jump_off", jumpOff
                , "count", patched.Length)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteFindUserFunc(h, layout, name) {
        list := AhkMagic._RemoteFindUserFuncs(h, layout, name)
        return list.Length ? list[1] : 0
    }

    static _RemoteFindUserFuncs(h, layout, name) {
        gscript := layout["gscript"]
        arr := AhkMagic._RPtr(h, gscript + layout["mfuncs_off"])
        count := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
        if count < 0 or count > 100000
            throw Error("mFuncs count out of range: " count, -1)
        result := []
        shortName := name
        dot := InStr(name, ".")
        fullName := ""
        if dot {
            shortName := SubStr(name, dot + 1)
            fullName := SubStr(name, 1, dot - 1)
                . ".Prototype." shortName
        }
        loop count {
            nf := AhkMagic._RPtr(h, arr + (A_Index - 1) * 8)
            candidates := [name, shortName]
            if fullName != ""
                candidates.Push(fullName)
            for want in candidates {
                data := AhkMagic._RemoteRead(h, nf, 0x500)
                found := false
                loop data.Size // 2 - StrLen(want) {
                    off := (A_Index - 1) * 2
                    ok := true
                    loop StrLen(want) {
                        if NumGet(data, off + (A_Index - 1) * 2, "UShort")
                            != Ord(SubStr(want, A_Index, 1)) {
                            ok := false
                            break
                        }
                    }
                    if ok and NumGet(data, off + StrLen(want) * 2, "UShort") = 0 {
                        found := true
                        break
                    }
                }
                if found
                    result.Push(nf)
            }
        }
        return result
    }

    ; Validate text with the interpreter's own /validate switch before it
    ; reaches the loader, so a syntax error becomes a clean library error
    ; carrying the real parse message instead of a dialog or an aborted
    ; host process (in-process EvalScript) or target (RemoteEvalScript).
    ; The validator is driven by its captured output and a completion
    ; marker file, never by process exit codes, and its process tree is
    ; killed the moment the error text lands so no build can leave a
    ; dialog on screen.  When no signal arrives within six seconds the
    ; check is treated as inconclusive and the loader's own failure check
    ; remains the fallback guard.
    static _ValidateScript(text, ahkPath) {
        if !ahkPath or !FileExist(ahkPath)
            return
        tag := Format("{:x}", A_TickCount) "-" Format("{:x}", Random(1, 0x7fffffff))
        vFile := A_Temp "\ahk_hack_validate_" tag ".ahk"
        errFile := A_Temp "\ahk_hack_validate_" tag ".err"
        batFile := A_Temp "\ahk_hack_validate_" tag ".bat"
        doneFile := A_Temp "\ahk_hack_validate_" tag ".done"
        try {
            ; #Warn off keeps warning text out of the captured stream so
            ; only real errors drive the throw below.
            FileAppend "#Warn All, Off`n" text, vFile, "UTF-8"
            ; The command line is routed through a batch file because AHK's
            ; Run re-parses nested quotes and can silently drop switches;
            ; inside the batch, cmd.exe receives the arguments verbatim.
            ; Format builds the quoted line (no manual quote escaping), and
            ; the batch is written without a BOM (cmd would otherwise fold
            ; the BOM into the first token).  /ErrorStdOut routes load
            ; errors to the captured stream so the validator never needs a
            ; dialog.
            batLine := Format('"{1}" /validate /ErrorStdOut "{2}" >"{3}" 2>&1'
                , ahkPath, vFile, errFile)
            FileAppend batLine "`n", batFile, "UTF-8-RAW"
            ; The done marker is only written after the validator exits
            ; (cmd runs batch lines sequentially), so it is a reliable
            ; completion signal - process handles/PIDs are not, because
            ; AHK's Run may surface an intermediate shell whose PID is
            ; immediately reused.
            FileAppend Format('echo done >"{1}"`n', doneFile)
                , batFile, "UTF-8-RAW"
            Run(Format('"{1}"', batFile), , "Hide", &vpid)
            ; The capture file is the primary signal: as soon as the error
            ; text lands, kill the whole validator tree (some builds still
            ; block on a dialog after writing it) and throw with the real
            ; parse message.  The done marker means the validator finished
            ; with no captured error: valid text.
            deadline := A_TickCount + 6000
            loop {
                size := FileExist(errFile) ? FileGetSize(errFile) : 0
                if size > 0 {
                    RunWait(Format('taskkill /pid {1} /T /F', vpid)
                        , , "Hide")
                    detail := Trim(FileRead(errFile, "UTF-8"))
                    if StrLen(detail) > 300
                        detail := SubStr(detail, 1, 300) "..."
                    throw Error("script validation failed: " detail, -1)
                }
                if FileExist(doneFile)
                    break
                if A_TickCount >= deadline {
                    ; No error text and no completion marker: kill the tree
                    ; and treat validation as inconclusive - the loader's
                    ; own rc check still guards the real load.
                    RunWait(Format('taskkill /pid {1} /T /F', vpid)
                        , , "Hide")
                    return
                }
                Sleep 20
            }
        } finally {
            ; The validator tree may still be releasing its file handles
            ; when this runs (taskkill returns before handles close), so
            ; cleanup races must never mask the real validation error.
            try {
                if FileExist(vFile)
                    FileDelete(vFile)
                if FileExist(errFile)
                    FileDelete(errFile)
                if FileExist(batFile)
                    FileDelete(batFile)
                if FileExist(doneFile)
                    FileDelete(doneFile)
            } catch {
            }
        }
    }

    static EvalSubprocess(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if expr = ""
            throw ValueError("expr must not be empty", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)

        tag := Format("{:x}", A_TickCount) "-" Format("{:x}", Random(1, 0x7fffffff))
        scriptFile := A_Temp "\ahk_mcode_eval_" tag ".ahk"
        resultFile := A_Temp "\ahk_mcode_eval_" tag ".txt"

        quote := expr
        script := "try {`n"
            . "    result := (" quote ")`n"
            . "    FileAppend result, `"" resultFile "`", `"UTF-8`"`n"
            . "} catch as e {`n"
            . "    FileAppend `"ERROR: `" e.Message, `"" resultFile "`", `"UTF-8`"`n"
            . "    ExitApp 1`n"
            . "}`n"
        FileAppend script, scriptFile, "UTF-8"
        try {
            ; Compiled exes built from the regular v2 runtime can re-enter
            ; interpreter mode with /script; the SC build does not support it.
            cmd := A_IsCompiled
                ? Format('"{1}" /script /ErrorStdOut "{2}"', A_AhkPath, scriptFile)
                : Format('"{1}" /ErrorStdOut "{2}"', A_AhkPath, scriptFile)
            RunWait cmd, , "Hide"
            if !FileExist(resultFile)
                throw Error("eval child produced no result", -1)
            result := FileRead(resultFile, "UTF-8")
            if RegExMatch(result, "^ERROR: (.*)$", &m)
                throw Error("eval failed: " m[1], -1)
            return result
        } finally {
            try {
                if FileExist(scriptFile)
                    FileDelete(scriptFile)
                if FileExist(resultFile)
                    FileDelete(resultFile)
            } catch {
            }
        }
    }
}

MC_INTERNAL_LOCATOR_X64 := "4157415641554154565755534881ec38030000488bb424a8030000c744242c000000004885c90f94c04885d2410f94c24108c24d85c0410f94c34508d34885f6410f94c2b8010000004508da0f854a0200004c8b9c24a00300004929d10f92c04929d3410f92c24108c2b8020000000f85270200004d8d9100000200b8030000004d39c20f8712020000498d41074c39d00f87000200004801ca4881c10000010031ff4c89c8eb180f1f840000000000488d58014883c0084c39d04889d8777c803c024875ea488d1c02807b018d75e0807b020d75da4c6373034c01f34883c3074839cb72ca4189ff4531f685ff74154a399cf430010000740b49ffc64d39f775ee4589fe4139fe400f95c583ff40410f93c44108ec75134a899cfc3001000042c744bc3000000000ffc74139fe73804489f3ff449c30e974ffffff85ff0f845301000089fb89d883e00383ff040f832202000031ff4531f631c94885c074384a8d1cf44881c3300100004e8d34b44983c6304531ffeb100f1f84000000000049ffc74c39f87410438b2cbe39cd76f04a8b3cfb89e9ebe84885ff0f84f6000000b8050000004981f8000100000f82e900000031c94889d3eb20660f1f4400004c8d71014881c10101000048ffc34c39c14c89f10f87c2000000803c0a4475e0807c0a018975d9807c0a024c75d2807c0a032475cb807c0a042075c441be04000000eb106666662e0f1f8400000000004983c60242807c33fc66752442807c33fd83751c42807c33fe3a751442807c33ff0074386666662e0f1f8400000000004981fe000100000f8473ffffff42807c33fd6675bb42807c33fe8375b342807c33ff3a75ab42803c330075a44c8d72ff4885c97418410fb6040e3dc3000000740e3dcc000000740748ffc975e831c94801d14531ffeb2eb8040000004881c4380300005b5d5f5e415c415d415e415fc3498d5f014983c706b8080000004d39c74989df77d742803c3ae875e44a8d043a486358014801d84883c0054839c875d04d85ff740f4c89fb41803c1ecc740748ffcb75f431db488d83000100004c39c077ae4801d3b804000000eb044883c002807c03fc667515807c03fd83750e807c03fe3a7507807c03ff007427483d000100000f8478ffffff807c03fd6675cd807c03fe8375c6807c03ff3a75bf803c030075b94d39d34d0f42d3498d41104c39d00f866d0100004531f6488d4a064531ffe99100000083e3fc31ff4531f631c9eb1b66666666662e0f1f8400000000004983c6044c39f30f84befdffff428b6cb43039cd760a4a8bbcf43001000089e9428b6cb43439cd760a4a8bbcf43801000089e9428b6cb43839cd760a4a8bbcf44001000089e9428b6cb43c39cd76b14a8bbcf44801000089e9eba54d8d5f014983c70648ffc1b8060000004d39c74d89df0f8795feffff42803c3ae875dd4a8d043a4c6360014c01e04829d04883c0054c39c875c64d8d5f05498d470a4c39c0400f97c5498d47654939c3410f93c34108eb75a741bb050000004d29e34531e442807c21ffe8750d4e632c214b8d2c234c01ed75264f8d2c274983c5064939c50f8375ffffff4f8d2c274983c50b49ffc44d39c576cae960ffffff4c8d4c242c4889d54889d14c89c24d89d0e80f01000085c00f84f90000004c01ed4c01fd498d042c4883c00a48893e4c89760848895e10488946188b44242c89462031c0e9c8fdffff4d89ceeb14498d46014983c6114d39d64989c60f877afeffff42803c324875e542807c32018975dd42807c32025475d542807c32032475cd42807c32041075c542807c32055575bd42807c32065675b542807c32075775ad42807c32084175a542807c320954759d42807c320a41759542807c320b55758d42807c320c41758542807c320d560f8579ffffff42807c320e410f856dffffff4983fe020f8263ffffff42807c320f570f8557ffffff42807c32ffcc0f854bffffff42807c32fecc0f853fffffff4901d6e9c8fdffffb807000000e9f0fcffff0f1f4000498d80000800004839d0480f42d04983c00431c0eb0d662e0f1f84000000000049ffc04939d0772842807c01fc8375f042807c01fe1075e8460fb65401ff458d5ace4180fb3277d8458911b801000000c3"
MC_MEM_SCRIPT_X64 := "5657534883ec204c89c64989ca488b4c24684d85d20f94c04885d2410f94c04108c04d85c9410f94c34508c34885c9410f94c0b8010000004508d80f85c80100008b4424600f57c00f11010f1141100f1141200f1141300f1141400f1141500f1141600f1141700f1181800000000f1181900000000f1181a00000000f1181b00000000f1181c00000000f1181d00000000f1181e00000000f1181f00000004c8d81000100000f1181000100000f1181100100000f1181200100000f1181400100000f1181500100000f1181600100000f1181700100000f1181800100000f1181900100000f1181a00100000f1181b00100000f1181c00100000f1181d00100000f1181e00100000f1181f00100004c8d1d0a0100004c89194c8d1d300100004c8959084c8d1d350100004c8959104c8d1d5a0100004c8959184c8d1d9f0200004c8959204c8d1da40200004c8959284c8d1da90200004c8959304c8d1dae0200004c8959384889890001000048c781080100000c00000049bb00000000b00400004c899910010000c781180100000200000066c7812c01000000000f1181300100004c898940010000898148010000c6814c010000004c898950010000898158010000c6815c010000004c8989600100004885f674048b3eeb0231ff4885f60f94c34889d14c89c24189f841ffd289c131c083f9010f95c108cb740688c801c0eb06ffc7893e31c04883c4205b5f5ec36666666666662e0f1f84000000000048c7414000000000c7414800000000c6414c0048c7415000000000c7415800000000c6415c0048c7416000000000c390b001c3666666662e0f1f84000000000048c7414000000000c7414800000000c6414c0048c7415000000000c7415800000000c6415c0048c7416000000000c390565753488b41404c8b51504c8b4960448b59484c29d04c01d84589c34c39d8410f43c085c074214189c083f808410f92c34889d64c29d64883fe200f92c34408db740d4531dbeb7e4531c0e9e300000083f82073054531dbeb424589c34183e3e031f6666666662e0f1f840000000000410f100432410f104c32100f1104320f114c32104883c6204939f375e34539c30f849d00000041f6c018742a4c89de4589c34183e3f8662e0f1f840000000000498b3c3248893c324883c6084939f375ef4539c3746d4c89c74c89de4883e703741e4c89de66662e0f1f840000000000410fb61c32881c3248ffc648ffcf75f04d29c34983fbfc773a0f1f8000000000450fb61c3244881c32450fb65c320144885c3201450fb65c320244885c3202450fb65c320344885c32034883c6044939f075cd4d01c24c8951504d01c14c8949605b5f5ec366662e0f1f84000000000031c0c3666666662e0f1f84000000000031c0c3666666662e0f1f840000000000488b4150482b4140c30f1f80000000008b4148c3"
MC_INPROC_EVAL_X64 := "4157415641554154565755534881ecf80000004c8ba424b00100004c8bbc24680100004c8b94246001000048c78424e00000000000400048c78424c8000000000000004885c90f94c04885d2410f94c34108c34d85c00f94c04d85c90f94c308c34408db4d85d20f94c008d84d85ff410f94c34883bc2488010000000f94c34408db08c34883bc2498010000000f94c04883bc24a001000000410f94c34108c34d85e4400f94c7b8010000004408df4008df40f6c7010f85b10900004d8b6c2470498b7424604d8b7424684983fd100f92c04d8d5e044d39eb410f97c3488d7e084c39ef0f97c34408db08c3b80a0000000f85760900004c89b424980000004889b424b8000000498b7424784d8bb424900000004889b424c00000004c8d5e084d39eb0f97c3498bb424a0000000488d7ebf4883ffcf400f92c74c89b424a000000049ffc64939f6400f97c54d8b9c24980000004008dd498d5b0448897424504839f30f97c34008fb4008eb0f85fb0800004c895c2470488bbc2480010000488bb42478010000498b8424800000004889842480000000498b8424880000004889442478498d81000100004885ff4989fb4c0f44d8498d99800100004d8db1000300004c89b424d80000004c89b424f00000004885f6490f44f10f85980000000f57c0410f1101410f114110410f114120410f114130410f114140410f114150410f114160410f114170410f118180000000410f118190000000410f1181a0000000410f1181b0000000410f1181c0000000410f1181d0000000410f1181e0000000410f1181f00000004d8b342441c60436034d8b74240841c60436014d8b7424104d85f6740841c70436010000004d8b7424184d891c360f57c00f11030f1143100f1143200f1143300f1143400f1143500f1143600f1143706641833a00741e4531f66666662e0f1f8400000000006643837c7202004d8d760175f3eb034531f648898c24900000004885ff4c899c24880000004889b424b00000000f85e0020000498d890008100048894c24680f57c00f11000f1140100f1140200f1140300f1140400f1140500f1140600f114070498b44242042c6041800498b44242842c6041801498b44243046893418498b4424384e891418498b44245042c7041800010000498b44245842c70418000100004531db4c89d54889e931c00f1f40000fb73c0185ff0f841e02000083ff27743e83ff2274398d779f6683fe1a0f82070100006683ff5f0f84fd0000008d77a56683fee50f87f00000000fb7f781fe800000000f83e10000004883c002ebb1488d2c014883c502440fb7740102664585f6410f94c7664139fe400f94c64408fe753966666666662e0f1f840000000000664183fe60750b66837d020074044883c502440fb775024883c502664585f6410f94c77406664139fe75d54584ff4c8bbc24680100000f856e0100004181fbfe000000775c4489de480faf7424504803742468488d3c014883c7024d89de4c8b9c248000000049893c334c8b5c247841c60433014c8b9c24a00000006641c7043303014d89f34889ef4829cf4829c74883c7fe48d1ef488b442470893c3041ffc34883c502e9cdfeffff4c895c24584c8d3c014531f64c89fdeb100f1f4400000fb77d024883c50241ffc68d779f6683fe1a72ec8d77bf6683fe1a400f92c66683ff5f410f94c34108f375d4440fb7df4181fb80000000410f92c38d77c66683fef6400f92c64184f374b54d39d7742f488b7c245881ffff000000410f92c366837c01fe2e400f95c64484de4989fb4c8bbc24680100000f843afeffffeb1a4c8b5c24584181fbfe0000004c8bbc24680100000f871efeffff448b5c24584c0faf5c24504c035c24684801c1488b8424800000004a890c18488b4424784ac7041800000000488b8424a00000006642c704180000488b442470468934184c8b5c245841ffc3e9cdfdffff4585db4c89d84c8b9c2488000000488bb424b0000000742189c0488b4c2450480fafc84c8b5424684ac7041100000000498b4424404e8914184c894c2458488954246849c707010000000f57c0410f1147084d8b304c894424504989304c8d8424c80000004889f14c89daff94249000000089c5488b8c24c80000004885c97407ff9424a001000083fd010f85e30100004883bc247001000000488b5424500f84e3010000498b442448488b8c2488000000488b3c084885ff0f84de0100004c897424604c8b9c24a8010000488b8424980000008b0407be010000004439d8488b4c24584c8b8424900000004c8b5424700f85b5010000488b8424900100004885c00f94c24c39c0410f94c04108d0488bbc2488000000488bac24b000000075164889e94889faffd0488b4c245883f8010f85a8020000488b9424d800000048899424e80000004c8bb4249800000041c7840e80010000ffffffff488b8424c000000048c7840880010000ffffffff4989940d8001000049c7840d88010000000000004881c100033f0048894c2438488d8424e00000004889442430488d8424f00000004889442428488d8424e8000000488944242048c744244000003f004c8d8424ac0000004889e931d24989d9ff542468488b4c2450488b5424604889114885c00f84d601000041c70700000000418b0c1e41894f04488b8c24b8000000488b0c1949894f0849894f10498d4f204889ca4829da4883fa0f0f87ec010000ba05000000440fb64413fb44884411fb440fb64413fc44884411fc440fb64413fd44884411fd440fb64413fe44884411fe440fb64413ff44884411ff440fb60413448804114883c2064883fa3575b6e9ae010000488b4424504c893041c70702000000e9590200004c893248b80000000063000000498907e9440200004c893241c70708000000e9350200004c8b8c24c00000004a8d2c0f4c8b8c24b80000004e8d340f4c01ef4803bc249800000049b9010000000080ffff4981c1010001004c898c24d0000000eb234c8b4c24784a8b04084989068b074c01ed4d01ee4c01ef48ffc64439d80f84eafdffff4881fe010001000f84de0000003d011000000f83a600000083f80475cc837d000277c6498b064885c074be49b9010000000080ffff4901c14c3b8c24d0000000727c4c8b8c24a000000042803c0807748c468b0410488b8c2480000000488b1408488b8c248801000041b903010000ff9424980100004c8b9c24a80100004c8b5424704c8b842490000000488b4c2458488b5424504885c00f8548ffffff488b44246048890241c70705000000e92201000041c70703000000e916010000488b44246048890241c70709000000e902010000488b442450488b4c246048890841c70706000000e9e9000000488b44246048890241c70707000000e9d50000000f10030f11010f1043100f1141100f1043200f114120418b4f0483f905774aba270000000fa3ca7340498b442448488b043849894750498b44244848833c38000f8481000000490faff54885f674784881fe20010000b820010000480f42c64883fe01751231c9eb4841c747040000000049894710ebb289c281e2fe01000031c94d8b4424484d8b0438450fb604084588440f584d8b4424484d8b0438450fb64408014588440f594883c1024839ca75d0a8017412498b442448488b04380fb604084188440f588b8424ac000000418987c800000031c04881c4f80000005b5d5f5e415c415d415e415fc3"