SuperAPI AI Directory

env-patch

Coding & development

View repository

Updated 10-16 08:46

About this skill

env-patch

For $ARGUMENTS , execute the environment patching plan.

Prerequisites: the encryption entry point is known (module ID, function name, script it resides in). If not yet located, first use /find-crypto-entry。

Core concepts

  • env_core.js is the engine: provides utility functions (setFuncNative / setObjNative / getNativeProto / wrapFunc / monitor / createProxy) and diagnostic reports. The engine contains no environment stubs, and is not modified after being copied.
  • run.js is the strategy: all environment stubs, patches, and loading logic are written in run.js. After each diagnostic round, only run.js is modified.
  • browser-stubs.md is the reference directory: fetch stub code as needed according to the UNDEFINED/ERRORS that appear in the diagnostic report.

Iron rules

  1. Do not modify the original JS — source/ : files underenv/ (main.js / sdk.js / bytecode.js) are generated, they are final and are no longer modified. All patches are written in run.js / sign.js.
  2. Must require env_core.js — rewriting utility functions such as setFuncNative / setObjNative / createProxy in run.js is prohibited. All tools are imported via const env = require('./env_core') , then call env.setFuncNative() / env.createProxy() , etc. env_core.js is not modified after being copied. If additional helpers are needed, write them at the top of run.js, but do not reimplement functionality that env_core already has.
  3. First analyze the VMP entry parameters — the dependency array of the VMP entry determines the boundaries of environment patching.typeof chrome !== "undefined" ? chrome : undefined checks globalThis, not window. You must use Object.defineProperty(global, name, ...) to synchronize.
  4. The loading order is fatal — VMP initializes and reads the environment at the require() ofinstant, and afterward it cannot be changed. The code in run.js must strictly follow this order:
    1. const env = require('./env_core') — get the tools (first line)
    2. Build stub objects (document / navigator / location, etc.)
    3. env.init({ window, document, navigator, location }) — block Node leaks + mount globals
    4. Additional environment completion — synchronize globals such as performance, chrome, etc.
    5. require('./main.js') — load the target JS
    6. Test the signature
  5. Format validation takes priority over request validation — the signature length/prefix being inconsistent with the browser = degraded; even an HTTP 200 is a false positive.

Template

File Purpose
${CLAUDE_SKILL_DIR}/scripts/env_core.js Toolset + Proxy engine + diagnostic report
${CLAUDE_SKILL_DIR}/scripts/webpack_runtime.js Minimal webpack runtime

Copy to project env/ after,env_core.js is no longer modified。

Execution flow

Step 1: Set up the project structure

<project>/
├── source/     # 原始 JS(下载,不修改)
├── env/        # 运行环境
│   ├── env_core.js      # 从模板复制,不改
│   ├── main.js          # 目标 JS 副本(或 sdk.js + bytecode.js)
│   ├── run.js           # 加载器 + 环境存根 + 测试
│   └── sign.js          # 签名接口(最后封装)
├── python/     # 验证脚本
└── docs/progress.md

Determine the scenario(read reference as needed):

Scenario JS file Reference document
Single-file SDK Copy to env/main.js —
SDK + bytecode separation env/sdk.js + env/bytecode.js references/multi-file.md
webpack bundle Extract the module to env/main.js references/webpack.md
Runtime dynamic loading curl download to source/, copy to env/ references/dynamic-loading.md

Step 2: First run

Write a minimal run.js containing only the necessary stubs:

const env = require('./env_core');
const _process = process; // init() 会隐藏 process,提前保留引用

// ① 构建最小存根(从 browser-stubs.md 取用 document/navigator/location)
const fakeDocument = { /* ... */ };
const fakeNavigator = { /* ... */ };
const fakeLocation = { /* ... */ };

// ② 组装 window
const fakeWindow = {
    document: fakeDocument,
    navigator: fakeNavigator,
    location: fakeLocation,
    // ... 按需补充
};
fakeWindow.window = fakeWindow;
fakeWindow.self = fakeWindow;
fakeWindow.top = fakeWindow;
fakeWindow.parent = fakeWindow;
fakeWindow.globalThis = fakeWindow;

// ③ 初始化(Node 泄露阻断 + 全局挂载)
env.init({
    window: env.createProxy(fakeWindow, 'window', 0),
    document: env.createProxy(fakeDocument, 'document', 0),
    navigator: env.createProxy(fakeNavigator, 'navigator', 0),
    location: env.createProxy(fakeLocation, 'location', 0),
});

// ④ 额外全局同步
// global.chrome = window.chrome;  // 如需

// ⑤ 加载目标 JS
require('./main.js');

// ⑥ 测试
console.log('签名函数:', typeof window.签名函数名);

Run node env/run.js, read the diagnostic report.

Step 3: Diagnostic loop (core)

After each round of running, read the diagnostic report and handle it according to the following decision tree:

诊断报告
├── [HANG] 进程卡死/无输出 → 参考 references/anti-debug.md
│   ├── setInterval debugger 死循环 → hook setInterval 过滤
│   ├── eval/Function 动态生成 debugger → hook eval + Function 剥离
│   └── 同步 while(true) → 定位源码,run.js 中 patch 全局
│
├── [ERRORS] → 必须立即修复
│   ├── TypeError: xxx is not a function → 补对应方法
│   ├── xxx is not defined → 补全局变量/构造函数
│   └── Cannot read property of undefined → 补中间对象
│
├── [UNDEFINED] → 逐项处理
│   ├── 先用 evaluate_script 从浏览器获取真实值
│   ├── 浏览器也是 undefined → 跳过(标记为已确认)
│   └── 浏览器有值 → 补到 run.js(从 browser-stubs.md 取用规范写法)
│
└── ERRORS = 0 && UNDEFINED 全部已确认 → 进入签名格式校验
    ├── 签名长度/前缀与浏览器一致 → Step 4
    └── 签名不一致 → 深度排查
        ├── monitor() 包装关键对象,追踪属性访问
        ├── 全局错误拦截:process.on('uncaughtException')
        ├── 对照 references/node-detection.md 逐项检查
        ├── 怀疑指纹检测 → references/fingerprint-consistency.md(跨层指纹自测清单)
        └── 所有外部 hook 均无异常 → references/limitations.md

Operations per round:

  1. Read the diagnostic report
  2. From browser-stubs.md take the corresponding stub code, and use env_core.js 's tools to write
  3. Only modify run.js
  4. Run again and return to Step 1

Step 4: Encapsulation verification

sign.js:

const env = require('./env_core');
// ... 环境构建(与 run.js 相同)...
require('./main.js');

module.exports = function sign(url, data) {
    // 调用签名函数,返回结果
    return window.签名函数名(url, data);
};

Verification order:

  1. Format verification — signature length and prefix match the browser
  2. Request verification — HTTP 200 + business data returned

Anti-patterns

  • Do not modify the original JS — source/ files under it are read-only,env/ the JS copy under it is final once generated. All patches are written in run.js.
  • Do not rewrite env_core's utility functions in run.js — setFuncNative / createProxy etc. must be imported from env_core; do not reimplement them.
  • Do not patch the environment after requiring the target JS — the VMP is initialized at the moment of require() , and patching the environment afterward is ineffective.
  • Do not treat HTTP 200 as a success indicator — if the signature format is wrong (length/prefix does not match the browser), even a 200 is a false positive.
  • Do not fill in all stubs at once — add them gradually according to the diagnostic report, modifying only run.js each round, and verifying by running each round.

Completion criteria

补环境结果:
- 诊断报告:ERRORS = 0, UNDEFINED 全部已确认
- 签名格式:与浏览器输出一致(长度、前缀匹配)
- 签名接口:sign.js 导出函数可直接调用
- 请求验证:HTTP 200 + 业务数据返回

References

Read as needed when encountering specific scenarios:

File When to read
references/anti-debug.md The process is stuck/extremely slow, or the target is known to have debugger anti-debugging
references/browser-stubs.md When stubs need to be added, to find the canonical way to write them
references/node-detection.md Signature degradation (length/prefix does not match the browser)
references/fingerprint-consistency.md The signature is correct but the request is intercepted; fingerprint detection is suspected; or Canvas/WebGL/AudioContext stubs need to be added
references/multi-file.md SDK + bytecode separation, multi-interpreter scenarios
references/dynamic-loading.md Encrypted code is dynamically loaded from an API
references/webpack.md Extracting webpack bundle modules
references/storage-tracing.md Suspecting that localStorage has a control-flow switch
references/limitations.md When all external hooks are ineffective, to understand the ceiling of the approach