Skip to main content
Glama

GameMod MCP

用元数据定位 Hook 落点的游戏 Mod 工具集 —— 一个 MCP 服务器,让任意通用 Agent(Claude Code / Codex / Cursor / …)都能做游戏 Mod。

支持 IL2CPP、Mono (.NET)、Unreal 三类引擎,45 个工具覆盖 「找游戏 → 建索引 → 查类/字段/方法 → 注入 → Hook → 读内存」全流程。


它解决什么问题

传统做法是拿内存修改器(Cheat Engine 式)反复搜值、改值、再筛。 问题在于:每一次都是在猜,游戏一更新全部作废,而且改完不知道为什么生效。

本工具集换一条路:游戏自己就带着完整的类型系统。

传统 CE 式

GameMod MCP

怎么找 "金币"

反复搜内存值、改值、再筛

查元数据拿到 UserData.gold 的字段名与类型,注入后取它的真实偏移

怎么找函数

AOB 特征码猜机器码

查元数据拿方法 token,注入后用 token 对账换算出真实地址

稳定性

游戏更新即失效

元数据变了才失效,且自动重建缓存

靠猜吗

靠猜(试错)

基本不靠猜(可枚举、可比对、可验证)

注意两侧的分工:静态元数据没有字段偏移、也没有方法地址(只有 token)。 偏移与地址一律来自注入后的运行时探针,token 是把两侧对上的钥匙。

具体做法是两段式:

  1. 离线解析元数据 —— IL2CPP 的 global-metadata.dat、Mono 的托管 dll。 拿到类型、字段、方法、token 的完整清单。

  2. 在线问运行时 —— 注入 Frida 探针,问游戏「这个字段真实偏移多少 / 这个方法真实地址在哪」。两边的 token 对上号,就是「我挂的确实是 我想挂的那个方法」的硬证据。

AOB 扫描(scan_pattern)保留,但只是符号被剥离时的兜底。


Related MCP server: Frida Game Hacking MCP

快速开始

1. 装依赖

pip install -r requirements.txt      # 只有 mcp + frida

引擎解析器(PE / IL2CPP / Mono)全部手写、只用标准库,不依赖 pefile / lief / 任何反编译框架。

一键安装(含冒烟测试):

python install.py          # 主推:纯 Python,Windows 自带 PowerShell 5.1 读不了
                           # 无 BOM 的 UTF-8 中文脚本(会语法报错),而 pwsh 不一定装了
python install.py --vision # 连可选弱视觉(截图 / OCR)一起装

本项目只支持 Windows。 install.ps1 仍保留,但只能用 pwsh(PowerShell 7)跑 —— 用 Windows 自带的 powershell(5.1)会因中文编码问题直接语法报错。

2. 接进你的 Agent

各客户端的现成配置在 mcp-config/,支持 Claude Code、Codex CLI、Cursor 以及任何标准 MCP 客户端。

以 Claude Code 为例,项目根目录建 .mcp.json:

{
  "mcpServers": {
    "gamemod": {
      "command": "python",
      "args": ["C:/path/to/GameModMCP/src/gamemod_mcp/server.py"],
      "env": {
        "PYTHONPATH": "C:/path/to/GameModMCP/src",
        "PYTHONUTF8": "1"
      }
    }
  }
}

3. 让 Agent 加载 skill

把 skills/gamemod-mod/ 放进你的 Agent 的 skill 目录。它包含完整工作流、各引擎差异、7 个配方(含 Unreal 路线)、以及一份 「查不到东西怎么办」的排查表——这是让 Agent 少走弯路的关键。

4. 开跑

你:帮我找到 Garden of Witches 里的存档服务,看看金币字段叫什么

Agent 会自动:
  get_diagnostics()         → 确认 Frida 可用
  inspect_game(...)         → 识别为 IL2CPP
  open_game(...)            → 建索引(首次约 2s,之后 10ms)
  search_fields("gold")     → 定位候选字段
  class_detail(...)         → 确认所属类与类型
  inject_process()          → 注入探针
  runtime_class_info(...)   → 拿真实字段偏移

工具一览(45 个)

本清单由 python devtools/gen_docs.py 从代码生成(来源:各工具函数的 docstring 首段 + server.py 的层节注释)。 不要手工编辑这一节 —— 下次 --check 会报红。 要改描述就改该工具的 docstring 首段,再跑 python devtools/gen_docs.py --write。

为什么改成生成:校验只能发现漂移,生成本不会产生漂移 —— 本表的工具数曾手工漂过 5 次以上。 改造前做过「知识上移」核对:原单元格里有 402 个特征词是 docstring 没有的, 直接生成会丢掉那些手写事实,所以先把它们补进了 docstring 首段。

发现层

工具

作用

find_game_processes

列出正在运行的游戏进程,并识别其运行时(il2cpp / mono / 其它)。

inspect_binary

分析 PE 文件(exe/dll):架构、节区、导出表、导入表、运行时类型。

inspect_game

探测一个游戏目录:识别引擎、找出主程序与关键文件。

会话层

工具

作用

close_session

关闭当前会话:卸载所有 Hook、断开注入、关闭索引。只有真的恢复原状才 ok=true。

get_session

查看当前会话状态:选中的游戏、索引统计、运行时注入情况。

open_game

选定游戏,并构建(或复用)它的元数据索引(跨引擎自动识别)。

静态查询层(不需要游戏在运行)

工具

作用

analyze_dependency

分析一个类型引用了哪些其它类型(字段/父类/方法返回类型)。近似,非精确调用图。

class_detail

查看一个类的完整信息:字段(含类型)、方法(含 token)、继承链;给 method_name 可只看某方法的重载。

class_hierarchy

查看类的继承体系(向上找基类、向下找派生类)。direction="down" 是逐层的:depth 是所在层、parent 是真实直接父类;单层超 500 条时 down_has_more=true。

decompile

反编译 Mono/.NET 程序集,拿到真正的 C# 源码(薄封装 ilspycmd)。

find_derived_classes

找出所有继承自某个基类的类型(含间接继承);配 assembly= 可筛出游戏自己的 MonoBehaviour 脚本(找脚本的必备工具)。

list_images

列出游戏里的程序集(dll)及各自的类型数量。支持 offset/limit 翻页并如实给 has_more。

search_classes

搜索游戏里的类、结构体、枚举 —— Mod 开发最主要的入口。支持按基类与程序集过滤。

search_fields

按名字搜索字段,返回所属类与字段类型。按字段名搜(定位「金币」「血量」这类目标的首选入口)。

search_methods

按名字搜索方法,返回所属类、返回类型与 token。owner 语义与 search_fields 一致。

结果句柄层(大结果集翻页)

工具

作用

read_query_result

按句柄翻页读取之前某次查询的完整结果;给了 pattern 就在结果内再过滤。

运行时层

工具

作用

drain_events

取走探针缓冲的 Hook 事件(调用记录)。kind 过滤作用于整条队列(不是只看队首)。

eval_script

在游戏进程内执行任意 Frida JS 代码 —— 应急逃生舱。

find_hook_target

定位某个方法可 Hook 的地址(含 RVA 与 token)。同样支持完整嵌套名,歧义时拒绝。

find_instances

枚举类的活对象实例(拿 instance 指针的正路 —— 此前只能靠 Hook 碰运气)。走 GC liveness;IL2CPP 可用(实测毫秒级)、Mono 受官方限制会如实报 unsupported(不用空列表冒充「没有实例」),值类型无堆实例则明确报错。

gc_control

暂停/恢复游戏的 GC(垃圾回收)。

hook_address

在任意地址安装 Hook(不限于托管方法)。适用于原生函数、系统 API、以及「模块基址 + RVA」算出的地址。

hook_method

在目标方法上安装 Hook,捕获它的调用(参数、调用来源、返回值)。用 token 指定重载;多候选未指定则拒绝。

hook_status

列出当前所有 Hook 及其触发次数。

inject_process

把运行时探针注入正在运行的游戏进程。目标换了游戏时会把会话身份与索引一起换过去,并在返回里给 context_switched。

launch_game

启动游戏并在游戏初始化之前注入探针(能抢在初始化前挂钩)。

read_field_value

读取某个实例或静态字段的原始值(静态字段传 instance="static")。读几个字节由字段类型决定(bool 1 / int 4 / long·double·引用 8)。

read_memory

读取游戏进程内存(十六进制返回)。

runtime_class_info

查类的运行时实况:真实字段偏移与方法真实地址(RVA)。

runtime_info

查看已注入游戏的探针状态:IL2CPP 模块、映像数量、可用 API。含静态/运行时一致性自检(image_count_matches)。

scan_pattern

在游戏模块里做 AOB(特征码)扫描。这是兜底手段 —— 优先用元数据 + RVA 定位。

set_field_value

写对象字段值(read_field_value 的对称入口):自动查偏移、按类型定宽度与编码、写完回读核对。溢出直接报错不截断;引用类型字段拒绝(改引用属改逻辑,请用 eval_script/Hook)。

unhook

卸载 Hook。hook_id 留空则卸载全部。

write_memory

写游戏进程内存。

弱视觉 / UI 层(截图 · OCR · 日志 · 等待)

工具

作用

capture_screen

截屏 / 截窗口存盘并返回路径(不回 base64)。截图是证据、不是判据;窗口找不到会如实失败,不静默改截整屏。

ocr

OCR 识别文本并给屏幕绝对坐标(可直接用于点击)。只对给定 ROI 做(实测整屏 4.1s vs ROI 数十 ms)。

probe_log

读游戏日志尾部并按正则过滤(编码自动 utf-8/gbk)。只读、不消费,可反复轮询。

verify_state

出一份可证伪的验收结论:每条断言独立求值,并要求足够多的独立证据才判成立。

wait_for

轮询等待条件成立并给出证据包(验收闭环里「等到可以判了」那一步)。五种条件:field / instances / ocr / log / window。返回三态 verdict(pass / fail / unverifiable —— 把「没法判」与「没通过」严格区分)+ polls / evidence;超时如实报 fail,不静默当通过。注意 kind="log" 等的是新增(先记基线再等增量),不是「当前存在」。

Unreal 引擎层

工具

作用

detect_ue

判断一个 exe 是否为 Unreal 引擎游戏,并给出可核查的证据与置信度。

ue_list_entries

列出 .pak / .utoc 里打包了哪些资产(可按名字过滤、翻页)。返回 returned/has_more;翻页空页会说明是哪一种「空」。

ue_manifest

读取 UE 的打包清单 Manifest_*Files*.txt。支持 offset/limit 并有 has_more。

ue_profile

UE 游戏概况:容器、清单、脚本模块。汇总一个 Unreal 游戏的可提取信息(资源容器、清单、脚本对象)。

ue_script_objects

从 UE 游戏 exe 里提取 /Script/ 模块名与对象路径。失败时按契约给 ok=false + error。

诊断层

工具

作用

get_diagnostics

检查运行环境:Python、Frida、各引擎解析器是否可用。默认不扫本机进程(见 scan_processes)。

句柄格式与作用域

句柄形如 il2cpp-g3-classes-1({引擎}-g{代次}-{种类}-{序号})。 代次段是正确性要求:句柄表是每个索引实例各自的、序号从 0 重来, 不带代次就会让旧句柄在切游戏后静默读到另一个游戏的结果集(实测过)。 所以切游戏或重建索引后旧句柄一律失效,失败时会返回 available_handles。 见 devtools/verify_handle_scope.py。

响应体积上限(256 KB)

任何工具返回都不会超过 256 KB —— 这不是各工具自觉,而是 src/gamemod_mcp/cap.py 在所有工具外面统一兜底(@_cap_response)。

超限时截断最大的那个顶层列表,并把完整列表登记成句柄:

  • 返回里多出 capped: true、cap_bytes,note 写明「只回了前 N 条」;

  • has_more 一律改成 true、returned 改成实际条数(不许明明还有却说没有);

  • note 里给出续读方式 read_query_result(handle=..., offset=N), 一条数据都不会丢 —— 完整列表在句柄里。

  • 已有 handle 的返回(search_*)复用它的句柄,不再另造一个; 此时续读的 offset 是「原 offset + 已回条数」,按「已回条数」算会跳过前面的条目。

为什么必须有:契约第 5 条原先只靠各工具的 limit 自觉遵守,但 limit 上限是 2000,Agent 完全可以合法地要 2000 条 —— 实测 search_classes('', limit=2000) 的线上文本是 856 KB。截断后同一调用是 260 KB。 见 devtools/verify_cap.py(机制)与 devtools/verify_response_size.py(体积)。

资源(MCP resources)

只有一个:gamemod://guide —— 这份 MCP 自己的完整使用指南 (与 skills/gamemod-mod/SKILL.md 同源,逐字节相同)。

gamemod://guide   text/markdown   约 24 KB

支持 resources 的客户端可以直接读它,不必先在系统提示里塞进这份长文档。 它也是全项目唯一没有工具出口的内容:其余(会话状态、引擎能力矩阵、 缓存与依赖清单)都已经由 get_session / open_game / get_diagnostics 返回,再开一个资源出口就是同一份数据的第二个来源(契约第 3 条)。

为什么没有 prompts

MCP 还有 prompts 这一面,本服务器不做,这是量过的结论而不是遗漏:

协议面

客户端(DSH)实测

结论

resources/list resources/templates/list resources/read

客户端实现了,且把资源包成给模型用的工具(模型够得着)

✅ 做

prompts/list prompts/get

客户端一个 prompt 方法都没有,也没有对应的包

❌ 不做

证据:在 E:\DSH\resources\app.asar 里按路径检索 JS, dsh-mcp-client 的请求分发 switch 里只有 resources/* 三个分支, prompts/* 零命中。加了 prompt 也传不到模型面前, 只会是一份没人读的声明 —— 与当初否决 outputSchema 是同一套判断方式 (见 docs/项目梳理与优化建议.md §4.14 附近)。

反过来说:换个客户端就要重新量一次。如果某个客户端确实消费 prompts, 「把 SKILL.md 挂成带 engine 参数的 prompt」是现成的做法。 devtools/verify_mcp_resources.py 已经把「资源清单恰好只有 guide」 做成断言,将来补资源时会强制过一次讨论。


人工调试前端(旁路设施)

给人工调试用:在浏览器里看有哪些工具、怎么调、返回什么, 并留下 AI 客户端到底调了什么的流水,便于回溯。

python devtools\debug_ui.py            # → http://127.0.0.1:8770/
python devtools\debug_ui.py --log-on   # 本次启动就打开调用流水
# 装过包之后也可以:gamemod-mcp-dbg-ui

它不重新实现任何工具:工具清单直接问 FastMCP.list_tools(), 入参表单由每个工具的 inputSchema 生成,调用直接走 FastMCP.call_tool()。 所以页面上看到的参数表、返回体与 AI 客户端拿到的是同一份, 不存在「前端能调通、AI 调不通」这种漂移;ok:false 的真实报错也照样显示。 左侧按 server.py 的八个「工具分层」分组,只读 / 破坏性 / 接触外部世界 直接取 MCP 的语义标注。

调用流水(可开关 / 可查看 / 可清理)

页面第二个页签是调用流水:每次调用记下工具名、参数、返回、ok、错误、 耗时、体积。默认关闭,页面上可以随时开关。

日志点落在服务器分发层,而不是前端里 —— 这一点是刻意的: 前端只能看见「经它发出的调用」,而 AI 客户端会自己拉起一个 MCP 服务器进程,前端根本看不见。记在 ToolManager.call_tool(FastMCP 分发工具的唯一入口)上,人工手调的(source: "ui")与 AI 调的 (source: "mcp")才会落进同一份流水,靠 source / pid 区分。 verify_debug_ui.py 用另一个进程的 stdio 客户端把这件事验成了断言。

环境变量

作用

默认

GAMEMOD_DEBUG_LOG

1 开 / 0 关(也可只给某个客户端单独开)

关

GAMEMOD_DEBUG_LOG_PATH

流水文件

~/.gamemod/debug/calls.jsonl

GAMEMOD_DEBUG_LOG_MAX_BYTES

单文件上限,超过则轮转

8 MB

GAMEMOD_DEBUG_LOG_MAX_FILES

保留份数(含当前)

4

三条约束都做成了断言:① 绝不影响业务 —— 记不上就不记(磁盘满 / 权限 不足 / 路径是文件),不许让一个本来正常的工具失败;② 不许无界增长 —— 超上限就轮转、只留最近几份,单条记录里的超大参数/返回截断并标 truncated; ③ 默认不占磁盘 —— 开关关着一个字节都不写。

这个前端不影响 MCP 业务:不起 stdio、不占 MCP 端口、不改任何工具实现, 关掉它 MCP 一侧完全不受影响。运行期依赖也没变(只用标准库 http.server,不引入 Flask/FastAPI/Node);对 server.py 的唯一改动 是一个默认关闭的流水钩子。详见 devtools/verify_debug_ui.py(61 项)。


目录结构

GameModMCP/
├── README.md                    本文件
├── pyproject.toml               打包配置(pip install -e . 可用)
├── requirements.txt             运行依赖(只有 mcp + frida)
├── install.py                   一键安装(**主推**)+ 冒烟测试
├── install.ps1                  同上,但**只能用 pwsh 7**(5.1 读中文会语法报错)
│
├── src/gamemod_mcp/
│   ├── server.py                MCP 服务器(45 个工具)
│   ├── debug_ui.py              人工调试前端(HTTP,旁路设施;见下节)
│   ├── debug_ui.html            该前端的界面(单文件,无外部依赖)
│   ├── debuglog.py              工具调用流水(可开关 / 可查看 / 可清理)
│   ├── logutil.py               日志(只走 stderr,stdout 留给 JSON-RPC)
│   ├── engine/                  静态分析层
│   │   ├── pe.py                纯标准库 PE/COFF 解析器
│   │   ├── discovery.py         进程枚举 / 引擎识别(不再做全机 Steam 扫描)
│   │   ├── il2cpp_meta.py       IL2CPP global-metadata.dat 解析器
│   │   ├── mono_meta.py         ECMA-335 元数据解析器(45 张表)
│   │   ├── ue.py                Unreal 提取器(pak / utoc / 清单 / 脚本对象)
│   │   ├── adapt.py             **引擎适配层**(让索引层不关心引擎差异)
│   │   ├── decompile.py         Mono 反编译薄封装(可选外部依赖 ilspycmd)
│   │   ├── index.py             SQLite 索引 + 结果句柄
│   │   └── state.py             会话状态(发现→索引→运行时的粘合层)
│   └── runtime/                 动态分析层
│       ├── agent.js             Frida 探针(IL2CPP 59 + Mono 56 个 API,双后端)
│       └── session.py           Frida 生命周期管理
│
├── skills/gamemod-mod/SKILL.md  Agent 使用指南
├── mcp-config/                  各客户端的 MCP 配置样例
├── agent_logs/                  子 Agent 委派记录(prompt + 产出)
└── devtools/                    验证脚本(见 devtools/README.md)
    ├── verify_*.py              验收脚本(对真实游戏与地面真值)
    ├── check_encoding.ps1       编码校验(UTF-8 无 BOM)
    ├── archive/                 归档的过程产物(不参与验收)
    │   └── probes/              一次性逆向侦察脚本(格式出问题时重启定位用)
    └── groundtruth/             真值来源(MonoGroundTruth / 官方 dump)

引擎支持程度

IL2CPP

Mono (.NET)

Unreal

静态查询类型

✅ 完整

✅ 完整

❌ 无类型系统可查

字段类型精度

⚠️ 启发式(~90%)

✅ 精确(ECMA-335 签名)

—

字段偏移

✅ 运行时取

✅ 运行时取(mono_field_get_offset)

❌ 需运行时,未实现

方法地址

✅ 运行时取(含 RVA)

✅ 运行时取(JIT 机器码,无 RVA)

—

运行时 Hook

✅

✅(地址在 mono 模块外,直接按地址挂)

—

GC 暂停

✅ il2cpp_gc_disable

❌ 本版 Mono 无 mono_gc_disable 导出

—

资源容器清单

—

—

✅ pak/utoc 100% 准确

可反编译出源码

❌(已转 C++)

✅(ILSpy/dnSpy 接近源码)

❌

两个引擎的运行时配方一致:注入 → runtime_class_info 取偏移 → find_hook_target 取落点 → hook_method + drain_events。 探针里是两套实现(il2cpp_* 59 个 API / mono_* 56 个 API), 对外工具与 token 对账方式完全相同(静态 token == 运行时 token)。 Mono 上有四条差异,都是如实声明而非「未实现」:

  1. 没有 RVA:地址来自 mono_compile_method 触发的 JIT, 代码不在 mono-2.0-*.dll 内,所以 in_module: false 且不给 rva; 元数据里的 RVA 指向 IL 字节码,不能拿来 Hook。

  2. gc_control 不支持:没有 mono_gc_disable / mono_gc_enable 导出(四个 Mono 样本一致核实),调用会明确返回 ok:false。

  3. 程序集懒加载:刚注入时只枚举到十来个程序集,UnityEngine.* 要过几秒才出现(实测 13 → 164)。注入成功后探针会等它加载稳定再返回 (判据是数量连续 4 次采样不变,实测等了 4.0 秒、看到 144 个), 结果放在 init.assemblies 里;返回里的 searched_images 也会如实给出当前数量,若仍未稳定可稍后重试。 为什么必须等(不是保险起见):矩阵实测 Archon Soul 在只有 13 个程序集时 被 Hook 并强制调用 get_frameCount,进程随后消失 —— 「注入成功」与「可以 Hook 托管方法」在 Mono 上不是同一件事。

  4. 静态索引的范围:Mono 的静态索引默认只收游戏自己的程序集 (Assembly-CSharp.dll),UnityEngine.* 这类框架类型静态查不到。 class_detail 会明确说明这一点并指向 runtime_class_info, 而不是只回一句「用 search_classes 搜短名」(那条路走不通,它压根没被索引)。

实测数据(真实游戏,非构造样本):

  • IL2CPP(Garden of Witches,17 MB 元数据): 20,072 类型 / 139,501 方法 / 81,872 字段 / 94 映像。 字段类型解析率 90.10%,与地面真值 dump 比对 27,264 条 0 错(100% 精度)。 建索引 2.6s,缓存复用 1.3s。

  • Mono(Archon Soul,Assembly-CSharp.dll): 1,387 类型 / 6,611 方法 / 6,343 字段。 与 Microsoft 官方 System.Reflection.Metadata 交叉验证 45 张表 0 差异, 每类型的全名/基类/字段名/方法名 0 差异,签名解析 100%。 运行时(Frida 注入同一游戏,实测): mono_* 导出绑定 56 个、零缺失(四个样本的 PE 导出表逐个核实); 程序集清单随游戏运行从 13 涨到 164(Mono 是懒加载,跨调用缓存会永久漏掉 UnityEngine.*);UnityEngine.Time 的 token 0x020002e4 与静态元数据 完全一致(Time.get_frameCount 0x06002468 亦然,独立复现了 System.Reflection.Metadata 真值);短名解析在 27,222 行类型定义里 0.1s 完成;Hook 上 get_frameCount 后捕获到真实返回值(游戏运行 14.5s 时 retval=872,与 60fps 自洽)。方法地址是 JIT 机器码,in_module=false, 因此没有 RVA(不给假数据);gc_control 如实报不支持。 另一个实测到的坑:游戏跑约 90 秒后,同一个程序集会被加载出两个映像, UnityEngine.Time 于是被枚举出两条行号/token/命名空间/程序集名全同、 只有 klass 指针不同的记录 —— 不按(程序集名 + 命名空间 + 短名)去重的话, 一次本可解析的查询会变成「命中 2 个同名类」,而调用方无法消解 (两条的 image / namespace / full_name 一模一样)。顺带一提, 内存加载的程序集 mono_image_get_filename 返回的是 data-00000219A9D5A040 这种合成名,不是路径,按路径去重不会命中。 第四个样本 Gambonanza 上复现并修掉了「注入太早就 Hook」这个缺陷: 修复前注入后 4 秒时只有 15 个程序集,Transform / Time 一律「没找到类」; 修复后注入即等到 144 个程序集稳定(4.0 秒),UnityEngine.Transform、 UnityEngine.Time、UnityEngine.GameObject 全部解析成功, find_hook_target 给出候选,10 秒后进程仍在。

  • Unreal(CatapultHero + Dinoblade 两个样本): .pak v11/v12 索引与 .utoc v8 目录索引完整解析, 与打包清单交叉校验 4,556/4,556、4,565/4,565、7,178/7,178、18,956/18,956 全部 100% 命中; 含 UTF-16 中文路径(字库、精灵片、角色)正确解码。 FName 池、属性偏移、.usmap 未实现——静态样本中不可得,诊断里如实标注。


可信度:怎么证明它是对的

这个项目不靠「看起来能跑」。devtools/ 下有 52 个可复现的验证脚本, 每个都对着真实游戏和地面真值跑(其中 43 个进自动套件, verify_tool_coverage.py 是静态导航图、不是门禁,故不参加):

脚本

验证内容

verify_parser.py

IL2CPP 元数据表结构与记录布局

verify_report_fixes.py

《重启验证与运行时理解报告》§4 那 5 个缺陷的修复:同步工具不许占住事件循环(分发路径必须换线程)、探针 RPC 必须有超时且卡死时不假成功、失败的 open_game 不许污染会话身份、ue_manifest 必须优先挑 UFS 清单、候选截断必须标 has_more。每条都带非空转反证(把实现改回旧写法必须报红)

verify_typedef_stride.py

typeDefinitions 记录大小:按版本号硬编码会错(v29 实测是 88 不是 92,用错会让 Σfield_count 变 6.5 亿、open_game 撞 UNIQUE constraint failed: fields.idx)。本脚本用合成元数据验证「字段区间恰好铺满 fields 表」这条判据本身、自验证改选必须 warn、并复现历史 bug(不依赖本机装了哪款游戏)

verify_index.py

索引层:构建 / 缓存复用 / 各类查询 / 结果句柄

verify_lazy_handles.py

结果句柄惰性物化:用解析条数断言证明翻页只解析一页

verify_handle_scope.py

结果句柄的作用域:跨游戏/重开索引的旧句柄必须被拒绝,不许读到另一个结果集

verify_session_switch.py

会话身份:切换游戏时上一个游戏的 exe 不许留下来(纯函数注入 + 真实 Mono→IL2CPP→UE 切换 + 进程级 stdio,含非空转反证)

verify_context_follow.py

上下文跟随目标:launch_game(exe=…) / inject_process(pid=…) 换了游戏时必须把会话身份与索引一起换过去(否则 image_count_matches=false,静态 token 与运行时地址属于两个游戏);反推不出游戏目录时不许硬猜、不许把上下文指到盘符根(C:\ 曾被误判为 Mono 游戏);对齐过程不许关掉刚注入的运行时。含非空转反证

verify_drain_kind_filter.py

drain_events(kind=…) 的过滤作用于整条队列,不是只看队首(旧写法遇到队首那条 notice(probe_ready) 就 break,实测在真游戏上返回 0 条而待取 201 条 —— 调用方会误判成「Hook 没触发」而去排查落点)。含不匹配事件不许被吞、超 limit 不许丢、顺序不变、kind="" 行为不变,以及把旧写法搬回来的反证

verify_mcp_discovery.py

目录布局判定:容器目录(装着多款游戏的父目录)不许被当成某一个游戏、UE 不许被送去建索引、不支持的引擎要给出家族提示(真实样本 + 合成布局 + 进程级 stdio,含非空转反证)

verify_subtype.py

继承过滤反过来查:与旧的逐条判定逐条相等 + 邻接表只建一次

verify_fieldtypes.py

字段类型解析率与精度(对 27,264 条 dump 真值)

verify_params.py

方法参数:v31 记录里哪个偏移才是 parameterStart(+12 恒定在表外、+16 单调且落在参数表内)、解析率、参数名精度(对 89,134 组 dump 真值 99.9%)、参数个数对账,含「改回旧偏移命中 0」的反证

verify_adapt.py

跨引擎:同一套查询 API 吃 IL2CPP + Mono

verify_mono.py

Mono 解析器,对官方 System.Reflection.Metadata 真值

verify_mono_coverage.py

Mono 覆盖到的表 / 特性范围

verify_mono_robust.py

Mono 对畸形输入的健壮性

verify_ue.py

UE 提取器对两个真实样本(pak / utoc,对打包清单)

verify_runtime.py

Frida 运行时层

verify_runtime_target.py

运行时定位 Hook 落点(token 对账)

verify_il2cpp_runtime.py

静态 token ↔ 运行时 token 交叉验证

verify_mono_runtime.py

Mono 运行时探针:56 个 mono_* 导出对四个样本的 PE 导出表逐个核实(含伪造名字反证)、int/uint32/bool 返回类型不许用 .toInt32()(一个真实崩溃的回归)、程序集清单按 RPC 刷新(13 → 164 的懒加载证据)、注入后等程序集加载稳定(用假会话测增长/恒定/一直增长/探针报错四种情形,并反证 IL2CPP 一次都不查)、真游戏上取 UnityEngine.Time 的真实地址与 token 对账、Hook + 事件、gc_control 如实报不支持

verify_mcp_tools.py

通过 MCP 工具层跑完整流程(IL2CPP)

verify_mcp_mono.py

通过 MCP 工具层跑完整流程(Mono)

verify_mcp_ue.py

通过 MCP 工具层验证 UE 能力真的接上了

verify_mcp_decompile.py

decompile 工具(ilspycmd 薄封装)

verify_mcp_extra.py

补全主流程没覆盖到的工具

verify_server_helpers.py

server.py 的纯函数:命令行拆分(launch_game)与字段读取宽度(read_field_value)

verify_field_write.py

字段写入的编码(set_field_value):宽度按类型、溢出必须报错不截断、浮点/整数不混、引用类型被拒、枚举字段按底层类型定宽、静态字段(instance="static")的分派与枚举成员前置拒绝。含非空转反证(改回「一律 8 字节」、去掉范围检查、静态判据不 strip/lower,断言必须报红)

verify_find_instances.py

实例枚举的语义(find_instances):max_count/namespace 真的传下去、派生类混入可核查(exact+derived==count;实测 Transform 全返回 RectTransform)、「拿不到」不许冒充「没有」(Mono 时机限制必须 unsupported)、只读标注。含非空转反证

verify_screen.py

弱视觉/UI 层(capture_screen/ocr/probe_log):ROI 原点加回坐标(用固定 OCR 桩做到确定性)、「没找到 ≠ 不存在」的语义、失败留证据、日志 GBK 自动探测与只读不消费、region 畸形输入。含非空转反证;缺可选依赖时记为「无法验证」而非通过

verify_wait_for.py

轮询等待与三态判定(wait_for):超时如实报 fail(不静默当通过)、unverifiable 与 fail 严格区分、timeout_ms/interval_ms 真的影响行为、条件后来成立时能等到、evidence 有上限但 polls 计数完整、kind="log" 等的是增量而非存在(旧日志已有时不许秒过)。含非空转反证

verify_verify_state.py

证据充分性契约(verify_state):单条证据不得判 pass(require_independent 默认 2)、三态区分、checks 写法校验、与 wait_for 共用同一套条件语义(_eval_condition)。含非空转反证(把要求降到 1 则同一输入变 pass)

verify_docs_admin_claim.py

不许再出现「注入需要管理员权限」这个错误说法(曾扩散到 8 处以上,源头是 session.py 失败提示里的一句猜测性归因;实测普通权限注入成功)。同时要求正面给出「不需要管理员」+ 权限级例外 —— 只删错误说法不留正确说法 = 丢信息。含非空转反证(用真实出现过的原句做样本)

verify_response_size.py

响应体积:P2-4 前后对比(用 S.RAW_TOOLS 调未套上限的原函数)、上限不变量、旧记录订正

verify_cap.py

体积上限的机制:截断/句柄/字段诚实性/不丢数据(纯函数 + 端到端两层)

verify_mcp_resources.py

MCP 资源层:gamemod://guide 与磁盘 SKILL.md 逐字节相同、资源清单恰好只有它、找不到文件时如实说明

verify_mcp_context.py

P2-3 的决策门禁(不是行为测试):钉住「进度无消费方、取消够不着、tasks 会弄坏」这三条否决依据,变了就报红

verify_stdio_entry.py

进程级入口:脚本形式(python <repo>/src/gamemod_mcp/server.py,且不带 PYTHONPATH)与模块形式都能握手;两种形式的工具清单一致;stdout 只有 JSON-RPC;脚本形式下真调 ue_* / decompile(惰性导入曾在这里炸)

verify_spawn_stdio.py

子进程 stdout 不许污染协议:launch_game 启动的游戏会往同一个 stdout 打 30 行日志(其中 "memorysetup-..." 是合法 JSON 字符串);断言产品用的是 stdio="pipe"、默认 inherit 确实会漏(反证)、pipe 不漏且 4 MB 洪水不会卡死子进程

verify_thread_attach.py

探针线程附着 IL2CPP 域:模块已加载 ≠ 域已建好(实测 6/9 个 IL2CPP 游戏上 il2cpp_domain_get() 返回野指针 0x135);断言不再「附着失败 → 记一笔就继续」(附旧写法反证)、wait_ready 重试契约(桩对象确定性验证)、真游戏上没有任何一次「ok=true 却带着附着失败」、附着后 il2cpp 调用可用且游戏进程仍活着、读野指针确实抛 access violation

verify_class_resolve.py

运行时类解析的三条规则:只给短名要能解析到顶层类型(老版本在 4 个游戏上报「未找到类 Transform」)、三条路(短名/显式 namespace/完整名)必须指向同一个类(比字段偏移表)、嵌套类型只认完整嵌套名(反证:7056 个嵌套类型里挑一个短名不撞顶层的,短名必须查不到、完整名必须查到)、不存在的类要给出 namespace 提示

verify_cache.py

索引缓存的生命周期(P3-2):只许删形状正确的文件(含反证)、三条护栏、命中时 touch、cache 统计与磁盘一致

verify_lazy_meta.py

元数据解析何时发生(P1-3b 的决策门禁):命中不解析不许退化、解析每次 open 只发生一次、逐题量哪些查询会逼出解析器、以及「载入失败不许被降级成『没有基类』」(含把旧写法搬回来的反证)

verify_merged_tools.py

合并后的工具:参数区分而不是各留一个

verify_agent_js.py

Frida 探针能编译、能加载、能 ping

verify_tool_inventory.py

文档与代码的工具清单、脚本清单一致

verify_tool_annotations.py

全部工具的语义标注:危险工具必须声明 destructiveHint;drain_events / gc_control 按《交接与评审说明书》§11.2 / §11.4 的取舍标注(含「drain_events 与 eval_script 必须能被客户端区分开」的反证对照)

verify_runtime_matrix_inventory.py

多游戏矩阵的工具清单对账必须阻断:report_runtime_matrix.py 的 ALL_TOOLS 与服务器实际报出的工具双向一致,且不一致时真的判失败(历史缺陷:该脚本漏了七个工具却只打一句警告,于是报告声称「逐名核对一致」而那七个从未被验证过)。含「删一个 / 加一个 / 服务器报 0 个」三种反证、三张分组的包含关系、以及每个工具都有执行阶段(不许出现「⬜ 没跑到」)

verify_tool_coverage.py

静态扫「哪个工具没人写测试」(导航图,会高估)

verify_debug_ui.py

人工调试前端与调用流水:日志坏路径不许拖累业务(含非空转反证)、前端工具目录与 MCP 协议及 server.py 分层三方对账、跨进程 stdio 客户端(模拟 AI)与前端落进同一份流水、文件轮转不许无界增长、旁路不改工具实现

verify_encoding_console.py

验收脚本在 GBK 控制台不崩

上表与 devtools/verify_*.py 的一致性由 verify_tool_inventory.py 校验, 所以它不会像以前那样悄悄腐坏(本节曾写「10 个脚本」而实际已有 20 多个)。

几个关键的「硬证据」:

  • 静态 token == 运行时 token(50/50、17/17、9/9 全中) → 证明确实挂到了目标方法,而不是同名重载。

  • PresetLoaderV2.userDataPresetFile 运行时偏移 = 0x20, 与地面真值 dump 里的 /*0x20*/ 一致 → 证明字段偏移是真的。

  • 运行时映像数 94 == 静态映像数 94 → 证明探针挂到的是同一个游戏实例。

  • 字段类型 27,264 条与 dump 比对 0 错 → 证明 derived 那一档没有蒙错。

跑法(一条命令跑全部,自带垫片、开跑前清空旧日志、结束时卡覆盖率门禁):

python devtools\run_all_verify.py

只跑一两个排查时用 --only(注意此时会跳过覆盖率门禁,因为子集不可能覆盖全部工具):

python devtools\run_all_verify.py --only verify_lazy_handles

改纯函数/解析逻辑时用 --fast(离线快检,不拉起任何游戏,约 3 分钟):

python devtools\run_all_verify.py --fast

--fast 跳过的是「真的会调用 launch_game / inject_process / spawn_and_attach / ensure_runtime」的脚本,清单由 AST 从源码推导 (不是手写名单 —— 手写清单必然漂移,正是本仓库反复吃亏的地方)。 它同时跳过动态覆盖率门禁(覆盖率要求整套跑完才有意义), 并在结尾如实列出跳过了哪些。 --fast 全绿 ≠ 全量通过:提交完整改动前请跑不带 --fast 的那条。

verify_mcp_tools.py 会真的启动一个游戏并在结束时结束它。 它需要 Frida 与一个 IL2CPP 样本(如 Garden of Witches)。 不需要管理员权限 —— 实测普通用户权限即可(见下方「权限」一节)。 样本不存在的脚本会记为「无法验证」并跳过 —— 跳过不算通过。

多游戏矩阵:能力边界不靠单一样本推断

单一样本只能证明「在某个游戏上能用」。devtools/ 下有两个跑遍全部样本 的报告脚本(只出报告、不改代码,退出码恒为 0):

python devtools\report_multi_game_matrix.py   # 静态:样本 × 工具的候选分类
python devtools\report_runtime_matrix.py      # 运行时:真的启动/注入/Hook,逐工具判定

report_runtime_matrix.py 会真的启动每个游戏、注入探针、装 Hook、 强制调用并读回参数与返回值,最后按「游戏 × 45 个工具」输出 ok / 不适用 / 失败 三态(--json 落盘 + --md 出报告)。 最近一次全量结果见 docs/多游戏运行时兼容性报告.md。

它对「正确拒绝」与「工具坏了」分得很清:decompile 在 IL2CPP 上返回 「不是 .NET 程序集」、短名有歧义时返回 ambiguous + 候选,都算 不适用 / 正确拒绝,不算失败 —— 否则报告会把设计行为统计成缺陷。

验收套件没全绿?先看失败清单

python devtools\run_all_verify.py 的汇总行只打印失败脚本的名字, 看不出「环境缺样本」还是「代码真坏了」——这两类必须分开处理。 逐项的现象 / 根因 / 修复路径 / 验证方法见 docs/验收失败清单.md。

该文件是滚动更新的:修掉一项就划掉一项。当前快照(2026-10-02)为 通过 33 / 40,7 项失败中 1 项是产品缺陷、2 项是脚本用了 Python 3.12 语法、4 项是缺样本。


设计取舍

为什么不做 MCP 的进度、取消与 tasks

这三样都是协议里有、但这个客户端不消费的东西,量过之后才决定不做:

协议面

实测

结论

notifications/progress

客户端调工具时只传 {signal, timeout},从不传 onprogress → 请求里没有 progressToken → report_progress 是静默 no-op;而且它是 async,同步工具根本调不了

不做

notifications/cancelled

客户端确实会传取消信号,但服务器这一侧够不着:RequestContext 没有任何取消字段、Context 也没有 cancelled;而且同步工具拿不到 context(本项目的工具体都不接受 ctx 参数),所以取消通知即便读到了也没有承接方

不做

notifications/message(MCP logging)

同样只有通用 SDK 里有,没有任何客户端包消费

不做

tasks 扩展(taskSupport)

客户端桥直接抛错:requires task-based execution, which this bridge does not support

不做(声明 required 会主动弄坏)

订正(旧文案已过时):本表曾写「FastMCP 对同步工具是内联调用、 事件循环被占满,连取消通知都读不到」,并据此说「60 秒的 launch_game 期间 服务器什么都干不了」。这两句现在都不成立:server.py 的 _install_event_loop_guard() 早已把经 MCP 分发的同步工具 offload 到线程池 (ToolManager.call_tool → asyncio.to_thread),并实测慢调用在飞时 get_session 10/10 次秒回(见《重启验证报告-第二轮》§4 与 devtools/verify_report_fixes.py)。 「不做取消」的结论不变,但理由换成了上面那条:结构上够不着 ——同步工具体拿不到 context,没有取消的承接方。

这三条依据由 devtools/verify_mcp_context.py 钉住:变了就报红, 提醒重读判断,而不是让断言变绿。

为什么引擎解析器全部手写,不用现成库

pefile / lief 更省事,但会带来三个问题:依赖体积、版本漂移、 以及遇到魔改文件时无从下手。自己写 PE 解析不到 400 行, IL2CPP 元数据解析 1,000 行——换来的是零依赖和完全可控的错误信息。

为什么要有 adapt.py 这一层

IL2CPP 和 Mono 是两个根本不同的元数据体系:

  • IL2CPP:字段/方法各自的索引轨道相互独立,字段的 typeIndex 甚至不在自己的索引空间里,得靠「比它小的最大 byval 键」推算(~90% 可解)。

  • Mono:标准 ECMA-335,类型都编码在 #Blob 签名里,能精确解出 完整类型名(数组、泛型、指针都对)。

但两者在「类型/字段/方法的归属关系」上是同构的。把类型引用的 表示差异收进 adapt.py,索引层就能共用一套代码——这也是 verify_adapt.py 存在的意义:证明同一套 API 两边都跑得通。

为什么宁可返回 ok: false 也不猜

工具返回必定带 ok 字段。索引层用 found=False 表示「查了但没有」, 服务器层会把它转成明确的 ok: false + error。

原因很直接:假成功是最危险的一类错误。如果「类不存在」返回 ok: true + 空列表,Agent 会以为类存在但没有字段,然后基于这个 错误结论继续往下走。宁可报错。

同理,IL2CPP v31 的字段记录里没有 flags 槽,所以 field_is_static 返回 None 而不是猜一个 False—— 猜出来的 False 会让 Agent 以为它验证过。

索引缓存怎么管(会一直长大的那个目录)

缓存文件名是 <引擎>-<元数据 sha256 前 16 位>.sqlite。这个键选得对 (换游戏、换版本必定重建,不会串数据),但它有个没人管过的副作用:

「玩过多少游戏」+「游戏更新过多少次」,都会永久堆在 ~/.gamemod/cache 里。实测一个 IL2CPP 游戏的索引 25.7 MB, 而 Mod 开发本来就要反复更新游戏 —— 每次更新换一个新文件, 旧文件再也不可能被命中。

以前这件事在任何工具返回里都看不见(get_diagnostics 只报一个路径字符串)。 现在:

能力

在哪

说明

看得见

get_diagnostics 的 cache、index_info 的 cache

{dir, files, total_bytes},现数现算的真数

有边界

打开索引时自动清理

上限 2 GiB;最近 4 个、7 天内用过的永不删

在用的不会被误删

命中缓存时刷新 mtime

否则天天在用的缓存会被当成「最久没用」先删掉

删了什么要说

index_info 的 cache_evicted

真删了才有这个字段;没删就不给(不给恒定噪声)

清理有三条护栏,而且只删文件名形状正确的(<引擎>-<16 位十六进制>.sqlite): 缓存目录里可能有你自己的东西,删文件不可逆,所以宁可漏删不可错删。 按上面的边界,5 个游戏的 ~130 MB 永远不会被碰。

想立刻腾空间就直接删 ~/.gamemod/cache 里的东西 —— 它只是可重建的派生数据, 代价是下次打开那个游戏重新解析(IL2CPP 约 2.6 秒,巨型样本约 12 秒)。 这条策略的验收在 devtools/verify_cache.py(含「把正则放宽就会误删」的反证)。

那「第一次查询」为什么还要等 400 毫秒

缓存命中确实不解析元数据(IL2CPP 528 → 19.6 ms),但紧接着的第一次查询 会把它逼出来:索引里存的是「有哪些类型」,而基类名、字段类型、方法返回类型 这些可读名字是查询时由解析器算的。实测 8 个查询里 7 个会逼出来 (只有 index_info 不用),IL2CPP 约 440 ms,在 5 查询的一次会话里占 65~78%。

把这些名字也存进 SQLite(路线图里的 P1-3b)能省掉这 440 ms。 已经量过,结论是不做,理由三条:

  1. 半量修复等于零。search_classes 需要解析器只因 parent 一个字段, 但「只存 parent_name」省不下任何东西 —— 紧接着的 class_detail 仍要解析。 解析是全有或全无,所以「parent_name/field_type 入库」这种按列拆的 写法本身就误导:这两列不能分别计价。

  2. 完整修复要动分层。要让 7 个查询都不解析,得把 class_detail / class_hierarchy / analyze_dependency 等 10 个查询层函数改成完全由 SQLite 驱动 —— 那等于在索引层再实现一遍继承语义,而 AGENTS.md §3 要求引擎差异只出现在 adapt.py。两份继承语义互相漂移,正是本项目反复 吃亏的那类错误。

  3. 省下的是「每次 open_game 一次」的 440 ms,不是每题 440 ms。

理由与数字钉在 devtools/verify_lazy_meta.py(既是回归门禁,也是这个决策的 门禁):哪天那张「哪些查询需要解析器」的表空了,它会提醒回来订正结论。

顺带,这个脚本还捞到并钉住了一个真缺陷:元数据载入失败曾被 except Exception 降级成「这些类没有基类」(ok: true + 200 行 parent: null)。 现在载入失败会如实报 ok: false + 原因,验收就是那段「把旧写法搬回来」的反证。


已知限制

  • IL2CPP 字段类型约 10% 解不出。这是元数据格式本身的限制 (复合类型只给到元素类型),不是 bug。工具会标记 type_confidence: unknown,需要时用运行时内存读取确认。

  • 反调试游戏注入会失败。带 EAC/BattlEye 的网游基本注入不了, 这是预期的,不要反复重试。

  • Unreal 支持受限。UE 没有像 IL2CPP 那样集中的元数据文件, 结构信息依赖 UE profile / .usmap。能力以 get_diagnostics() 报告为准。

  • Windows 注入不需要管理员权限(实测订正)。 只要 Agent 与游戏的完整性级别相同即可 —— 普通权限的 python 注入普通权限的 IL2CPP 游戏实测成功。唯一的例外是 游戏以管理员身份运行(完整性级别 High),此时才需要同样提权; 反之游戏是普通权限、Agent 是管理员,通常也能注入。

    这条曾经写错成「通常需要管理员权限」,来源是失败提示里的一句 猜测性归因。实测证据:本进程与游戏进程的完整性级别都是 Medium,attach → runtime_info → find_instances 全部成功。

  • Headless 环境验证不到自然调用。没有显示/GPU 的机器上游戏不渲染帧, 每帧方法不会自己触发。Hook 有效性通过从注入侧主动调用来证明。


环境要求

  • Windows(本项目只支持 Windows)

  • Python ≥ 3.10(实测 3.13.1)

  • mcp ≥ 1.0, < 2、frida ≥ 16(实测 frida 17.17.0)

⚠️ mcp 必须 < 2:本代码用 1.x 的 mcp.server.fastmcp.FastMCP, 2.x 已改名成 MCPServer(mcp.server.mcpserver)。只写 >=1.0.0 会让全新环境装到 2.x 然后服务器起不来 —— 这是打包分享包时实测到的。

可选弱视觉(截图 / OCR):dxcam|mss + rapidocr-onnxruntime|pytesseract

  • numpy/opencv-python/pillow/pywin32。不装不影响其它工具, get_diagnostics() 的 screen 段会如实报告可用性。


分享包(给别人测试用)

一条命令生成一个自带依赖、开箱即用的分享包:

python devtools\build_share.py            # 打包(会下载 wheel)
python devtools\build_share.py --no-wheels  # 只打源码(体积小)
python devtools\build_share.py --check      # 只校验已有分享包

产物落在仓库同级 ..\GameModMCP-share\(不进 git),并自动打 zip。 形态:

GameModMCP-share/
├── 安装说明.md      ← 给 AI 读的入口(**整个目录直接交给对方的 Agent**)
├── install.py       ← 一键安装(主推)
├── src/ skills/ mcp-config/ devtools/ docs/
└── wheels/required|optional   ← 依赖 wheel(离线可装,必需约 63 MB)

打包后自检 44 项,其中最关键的一条是用包内源码真起一次服务器 数工具数 —— 它能抓到「拷漏了文件」「依赖装错版本」这类只有对方才会踩到的 问题(实测就是靠它发现 mcp>=1.0.0 会装到 2.x 导致起不来)。 另有 6 项专门盯许可:LICENSE 在不在、是不是完整的 MIT 文本、有没有版权行, 以及 README 与 pyproject.toml 是否和它说同一件事 —— 因为分享包会分发 src/,而 COPY_FILES 里文件名写错时是静默跳过的,包照样打得出来、 照样能装,只是没有许可。

为什么入口是 Python 而不是 .ps1:Windows 自带的 PowerShell 5.1 读无 BOM 的 UTF-8 中文脚本会按 GBK 解码、直接语法报错,而 pwsh 对方不一定装了。对方装了 Python 才能跑 MCP,所以 Python 一定在。


许可

MIT © 2026 liwenhao0427

可自由使用、修改、再分发(含商用),只需保留版权与许可声明。 完整授权文本见 LICENSE —— 它与 pyproject.toml 的 license 字段是同一份许可,改动时必须同步。

为什么之前只有这一行字、没有文件:README 与 pyproject.toml 都声明了 MIT,但根目录一直没有 LICENSE,GitHub 因此把本仓库识别成 license: null —— 「声明了许可却没有授权文本」。分享包会分发 src/,所以许可文本必须 随源码一起走(见 devtools/build_share.py 的 COPY_FILES)。

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    Not graded
    maintenance
    Enables AI systems to interact with mobile and desktop applications through Frida's dynamic instrumentation capabilities. Provides process management, device control, JavaScript REPL execution, and script injection for runtime analysis and reverse engineering.
    14
    2
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    A comprehensive MCP server that exposes Frida's dynamic instrumentation toolkit to AI agents for process management, script injection, and memory operations. It provides over 50 tools to interact with local and mobile devices, enabling advanced capabilities like function hooking and memory analysis.
    55
    MIT