一键导入
mobai-reverse
MobAI Pro 会员系统逆向工程全流程。适用于 Go 64-bit PE 分析、license/quota 系统 patch、NSIS→7z SFX 重打包、以及 Go 二进制 embed.FS 前端提取。需要 IDA Pro MCP 连接。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
MobAI Pro 会员系统逆向工程全流程。适用于 Go 64-bit PE 分析、license/quota 系统 patch、NSIS→7z SFX 重打包、以及 Go 二进制 embed.FS 前端提取。需要 IDA Pro MCP 连接。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Create and iteratively refine parametric 3D CAD models through progressive refinement with inspectable feedback. Use when the task involves OpenSCAD, build123d, STL/STEP/3MF output, fixture/enclosure/adapter design, or debugging shape accuracy from renders or screenshots. Covers both generation and editing as a unified workflow.
Claudeception is a continuous learning system that extracts reusable knowledge from work sessions. Triggers: (1) /claudeception command to review session learnings, (2) "save this as a skill" or "extract a skill from this", (3) "what did we learn?", (4) After any task involving non-obvious debugging, workarounds, or trial-and-error discovery. Creates new Claude Code skills when valuable, reusable knowledge is identified.
AI-assisted distributed systems verification with TLA+ formal specification. Use when verifying distributed system correctness, discovering race conditions, writing TLA+ specifications from codebases, performing model checking on consensus/replication protocols, or setting up Lamport Agent workflows. Triggers on: "verify distributed system", "formal verification", "TLA+ spec", "model checking", "find race condition", "prove correctness", "CRAQ", "Paxos verification", "Raft verification", "chain replication verification", "distributed consensus verification".
Expert tutor and reference for 具身智能 / embodied intelligence and robot learning, grounded in 《具身智能导论》— the 17-chapter Chinese textbook translated from UC Berkeley CS294-291 "Robots That Learn". Use this skill whenever the user asks about embodied AI / robot learning topics the book covers: 中枢模式发生器(CPG)、 生物运动力学与步态、李群 SO(3)/SE(3) 机器人运动学与动力学、扩散模型/扩散策略 (DDPM, score matching, flow matching, diffusion policy)、灵巧手与欠驱动协同、 触觉感知(GelSight)、运动控制(LQR/MPC/阻抗控制)、卡尔曼滤波与预测控制、视频世界模型、 强化学习(MDP/SAC/PPO/actor-critic)、行为克隆与动作分块、视觉模仿学习(UMI/跨具身)、 足式运动(RMA)、导航(GOAT/可穿越性)、Sim-to-Real 灵巧操作、VLA 与 π0.5 长程规划。 Trigger it for conceptual questions, math derivations, paper explanations, PyTorch implementation help, study/teaching, or "解释一下/推导/这门课/具身智能" requests in this domain — even when the user doesn't name the book. Prefer this skill over answering from memory, because it gives grounded, citable, notation- consistent answers and links to the exact chapter.
Automates publishing novel chapters to 番茄小说 (fanqienovel.com) via Playwright browser automation. Use when the user wants to publish, batch-upload, or retry uploading novel chapters from a local directory to 番茄小说. Handles login state, ProseMirror editor injection, popup dismissal (错别字检测/仅基础检测/AI声明/确认发布), and auto-archives successfully published files. Triggers on requests like "发布章节到番茄", "上传小说", "batch publish chapters", or retrying failed chapters.
Use when reviewing HarmonyOS (ArkTS/ArkUI) code, when file extensions are .ets/.ts in HarmonyOS context, or when checking ArkUI component implementations, async/await patterns, or @State/@Prop/@Provide usage
| name | mobai-reverse |
| description | MobAI Pro 会员系统逆向工程全流程。适用于 Go 64-bit PE 分析、license/quota 系统 patch、NSIS→7z SFX 重打包、以及 Go 二进制 embed.FS 前端提取。需要 IDA Pro MCP 连接。 |
目标:MobAI Pro (Android 设备自动化工具) — 绕过 Pro 会员验证,解锁无限设备、无限 token、离线模式。
二进制: MobAI_payload.exe (145 MB, Go 1.22+ 64-bit, NSIS v3.11 Unicode 安装器内嵌)
补丁数: 7 (v2.0) | 方法: 根源数据覆写 + 服务器同步阻断
# 检查 EXE 签名
file MobAI.exe
# "Nullsoft Installer self-extracting archive (NSIS)"
# 提取 NSIS 载荷
7z x MobAI.exe -onsis-extract
# 产出:
# MobAI_payload.exe ← 真正的 Go 二进制 (145 MB)
# $PLUGINSDIR/System.dll, nsDialogs.dll
# $PLUGINSDIR/webview2bootstrapper/MicrosoftEdgeWebview2Setup.exe
file MobAI_payload.exe
# "PE32+ executable (console) x86-64 Go BuildID=..."
# 用 IDA 加载 → 17 个段,带 Go PCLNTAB 符号表
# .symtab 段: 函数名、包路径完整保留 (15 MB 符号表)
Go 的 embed.FS 数据存储在 .rdata/.data 段中。使用 IDA MCP 搜索特征路径:
# IDA Python: 搜索 "frontend/dist/"
results = ida_search.find_text("frontend/dist/", ...)
# 定位到 embedded.resources.Init() 中 embed.FS 引用
# 前端文件路径模式: frontend/dist/assets/index.xxxxxxxx.js
# 从 .rdata/.data 段提取: index.html, index.js (499KB), index.css (63KB)
提取的文件:
| 文件 | 大小 | 技术栈 |
|---|---|---|
index.html | 371 B | WebView2 入口 |
index.js | 499,662 B | React 18 + TypeScript + Webpack 5 + Zustand |
index.css | 63,833 B | Tailwind CSS + Nunito font |
scripts.js | 100,000 B | 辅助逻辑 |
auth-success.html | 485 B | Supabase OAuth 回调 |
Go 编译器在 .symtab 段生成完整符号表。IDA 自动解析为函数名 package.(*Type).Method。
关键命名空间:
mobai_internal_license.(*Manager).* ← 许可证核心
mobai_internal_quota.(*Manager).* ← 配额/限额
mobai_internal_supabase.(*Client).* ← 后端认证
mobai_internal_device/android/* ← ADB 集成
mobai_internal_embedded.* ← 内嵌资源
main.(*App).* ← 应用入口 + Go↔JS 桥接
# IDA MCP: 列出 license 包函数
ida_func_query(filter="mobai_internal_license.*")
核心函数表:
| 函数 | 地址 (VA) | 说明 |
|---|---|---|
NewManager | 0x140F311C0 | 初始化 license Manager |
GetTier | 0x140F31500 | 读取 tier 字符串 |
IsPro | 0x140F31620 | 判断是否 Pro |
HasUnlimitedTokens | 0x140F31680 | 无限 token? |
HasUnlimitedDevices | 0x140F316C0 | 无限设备? |
HasOfflineMode | 0x140F31720 | 离线模式? |
CanMakeRequest | 0x140F31780 | 允许发请求? |
CanConnectDevice | 0x140F31A00 | 允许连设备? |
GetStatus | 0x140F35020 | 向前端报告状态 |
SyncLicense | 0x140F32000 | 从服务器同步 |
RefreshTier | 0x140F33080 | 刷新 tier |
StartTierSync | 0x140F33EC0 | 启动定时同步 |
从 NewManager 和 GetTier/GetLimit 反推出内存布局:
// license.Manager (大小 0x60+)
// VA 参考: NewManager = 0x140F311C0
struct LicenseManager {
void* supabaseClient; // +0x00: Supabase 客户端
void* quotaManager; // +0x08: quota.Manager 指针
string tier; // +0x10: tier 字符串 (ptr)
int64 tierLen; // +0x18: tier 长度
void* rwMutex; // +0x20: 读写锁
// +0x28: EventEmitter
// +0x38: context/其他
};
// quota.Manager (大小 0x88+)
// VA 参考: NewManager = 0x140F2DEE0
struct QuotaManager {
void* supabaseClient; // +0x00
void* eventEmitter; // +0x08
void* storage; // +0x10
// ...
string tier; // +0x38: tier 字符串 (ptr)
int64 tierCode; // +0x40: tier 代码 (4)
int64 tokensRemaining; // +0x48: 剩余 token 数 (初始 100)
int64 limit; // +0x50: 每日限额 (100 = free, -1 = pro)
// +0x58: 离线票
void* offlineTicket; // +0x68: 离线 ticket
};
App 启动
└─ license.NewManager()
├─ tier = "free", len = 4 [地址: 0x140F3137D, 0x140F3138C]
└─ quota.NewManager()
├─ tier = "free", code = 4 [地址: 0x140F2DFEC, 0x140F2DFFB]
├─ tokensRemaining = 100 [地址: 0x140F2DFFF]
└─ limit = 100 [地址: 0x140F2E007] ← ROOT CAUSE
用户登录 → handleInitResult()
├─ 成功路径: 不调用 setTier (tier 保持 "free")
├─ 失败路径: setTier("free", 4) [地址: 0x140F328A0, 0x140F329C0]
└─ setTier() → quota.SetTier()
└─ 只更新 +0x38, +0x40, 不更新 +0x50! ← 设计缺陷
服务器同步 → StartTierSync() 定时调用 RefreshTier()
└─ RefreshTier() → SyncLicense() → 从 Supabase 获取真实 tier
└─ 如果是 free 用户 → setTier("free", 4) → 覆盖!
// IsPro @ 0x140F31620 — 反编译
bool IsPro(Manager* m) {
Tier t = GetTier(m); // 从 +0x10 读 tier 字符串
return t.len == 3
&& *(uint16*)t.ptr == 0x7270 // "pr" (LE)
&& *(uint8*)(t.ptr+2) == 0x6F; // "o"
}
// HasUnlimitedTokens @ 0x140F31680 — 反编译
bool HasUnlimitedTokens(Manager* m) {
return GetLimit(m->quotaManager) < 0; // limit == -1 → true
}
// CanMakeRequest @ 0x140F31780 — 反编译
bool CanMakeRequest(Manager* m) {
if (GetLimit(m->quotaManager) >= 0) // limit >= 0 → 受限
return CanUseToken(...); // 检查并消耗 token
return true; // limit < 0 → 无限制
}
// CanUseToken @ 0x140F2E3E0 — 反编译 (直接读字段!)
bool CanUseToken(QuotaManager* q) {
if (q->limit >= 0) // +0x50 字段
return q->tokensRemaining > 0; // +0x48 字段, 每次 -1
return true; // limit < 0 → 无限
}
策略: 逐个修补布尔检查函数 (IsPro, HasUnlimitedTokens, HasOfflineMode...)
失败根因:
CanUseToken 读 +0x50 字段 (100),不调用 GetLimit()策略: 直接修改数据源 + 阻止服务器覆盖
| # | 补丁 | 虚拟地址 | 原始字节 | 补丁字节 | 作用 |
|---|---|---|---|---|---|
| 1 | GetLimit 返回路径 1 | 0x140F2FC85 | 48 8B 44 24 10 | 6A FF 58 90 90 | push -1; pop rax (5B) |
| 2 | GetLimit 返回路径 2 | 0x140F2FC95 | 48 8B 44 24 10 | 6A FF 58 90 90 | panic 恢复路径 |
| 3 | license.NewManager len | 0x140F3137D | 48 C7 40 18 04... | 48 C7 40 18 03... | tier 长度 4→3 |
| 4 | "free" 字符串覆写 | 0x1418D15B8 | 66 72 65 65 | 70 72 6F 00 | "free"→"pro\0" |
| 5 | quota.NewManager limit | 0x140F2E007 | 48 C7 40 50 64... | 48 C7 40 50 FF... | limit 100→-1 |
| 6 | RefreshTier 禁用 | 0x140F33080 | 49 3B 66 10... | 31 C0 31 DB C3... | 立即返回 |
| 7 | StartTierSync 禁用 | 0x140F33EC0 | 4C 8D 64 24 F8... | C3 90 90 90... | 立即返回 |
原始指令 5 字节 mov rax, [rsp+0x10]。需要替换为 rax = -1。
mov rax, -1 = 48 C7 C0 FF FF FF FF (7B) → 超长!
解决方案: push -1; pop rax = 6A FF 58 (3B) + NOP × 2 = 5B ✓
6A FF push -1 ; -1 压栈 (sign-extended imm8)
58 pop rax ; rax = 0xFFFFFFFFFFFFFFFF
90 90 nop; nop ; 填充
31 C0 xor eax, eax ; rax = 0 (nil error → "成功"但无操作)
31 DB xor ebx, ebx ; rbx = 0 (nil 返回值)
C3 ret ; 立即返回 — 不调用服务器!
90×4 nop ; 填充
关键: Go 函数返回 (result, error) 在 rax, rbx 中。xor eax,eax; xor ebx,ebx; ret 等价于 return nil, nil ——调用方认为刷新成功但无数据返回,不会覆盖本地 tier。
NSIS 安装器无法直接修改后重新打包(需要 NSIS 编译器),使用 7z SFX 替代:
# 1. 用打补丁的 MobAI_payload.exe 替换原始文件
cp MobAI_payload.exe nsis-extract/MobAI.exe
# 2. 创建 7z 归档
7z a -t7z mobai-patched.7z ./pkg/* -mx=5
# 3. 拼接 SFX 安装器
cat 7z.sfx config.txt mobai-patched.7z > MobAI-Pro-Patched-v2.exe
;!@Install@!UTF-8!
Title="MobAI Installer (Pro Patched)"
BeginPrompt="This will install MobAI with Pro membership unlocked..."
Progress="yes"
RunProgram="MobAI.exe"
AutoInstall="yes"
Directory="MobAI"
;!@InstallEnd@!
补丁脚本需要正确处理 VA→文件偏移。PE 布局 (17 个段):
.text: RVA 0x1000 → file 0x600 (120 MB)
.rdata: RVA 0x1271000 → file 0x1270600 (100 MB)
.data: RVA 0x76A7000 → file 0x76A6000 (8 MB)
动态解析 远比硬编码偏移可靠——patcher.py v2.0 实现了完整的 PE 解析器。
WebView2 通过 window.chrome.webview.hostObjects.mobai 暴露 Go 方法:
// JS → Go (同步/异步调用)
await mobai.getLicense(); // → main.(*App).GetLicenseStatus → license.GetStatus
await mobai.connectDevice(sn); // → main.(*App).ConnectDevice
await mobai.runTask(taskJSON); // → 自动化执行
await mobai.upgradePro(); // → CreateCheckout
// Go → JS (事件推送)
window.chrome.webview.addEventListener('message', (e) => {
const { type, payload } = JSON.parse(e.data);
// license:updated, bridge:started, agent:progress
});
main.GetLicenseStatus (+0x1411C60E0)
└─ license.GetStatus (+0x140F35020)
├─ GetTier() → {ptr, len} → result._r0, result._r1
├─ IsAuthenticated() → result._r2
├─ GetUserEmail() → {ptr, len} → result._r3, result._r4
├─ if GetLimit() >= 0: tokens → result._r5, result._r6
└─ GetOfflineTicketExpiry() → result._r7
前端直接使用 tier 字符串显示 "Free"/"Pro" 标签。因此必须修改字符串本身而非仅 IsPro 返回值。
| 工具 | 用途 |
|---|---|
| IDA Pro 8.x + MCP 插件 | 反编译、反汇编、数据流追踪 |
| 7-Zip 23+ | NSIS 解包、7z SFX 打包 |
| Ghidra (备选) | Go PCLNTAB 符号恢复 |
| Python 3.11 | 补丁脚本、PE 解析 |
| Git + Git LFS | 二进制文件版本管理 |
CanUseToken 直接读 +0x50 而非调用 GetLimit()"pro" (3B) vs "free" (4B),需要同时修改长度字段RefreshTier/StartTierSync 必须禁用,否则服务器会覆盖本地修改push imm8; pop reg = 3B) 在空间受限时非常有用