// 在 Node 里跑 encrypt.js 的 webpack chunk,拿到 window.Jose 并实测 $encrypt。 // encrypt.js = (window.webpackJsonp||[]).push([["encrypt"],{"088e":factory}]) // 我们 stub window + 拦截 push 取出 088e factory,用 stub require 执行它。 const fs = require("fs"); const path = require("path"); const vm = require("vm"); const encPath = path.join(__dirname, "..", "out", "captcha_net", "js_07_rm_static_captcha_js_encrypt_ee7d2a41_js.js"); const src = fs.readFileSync(encPath, "utf-8"); const win = {}; const ctx = { window: win, self: win, console, Uint8Array, ArrayBuffer, Uint32Array, Uint16Array, Int8Array, Int32Array, Uint16Array, Float32Array, Float64Array, DataView, Buffer, setTimeout, clearTimeout, setImmediate, Promise, Math, Date, JSON, Object, Array, String, Number, Boolean, RegExp, Error, Function, Symbol, Reflect, Proxy, Map, Set, WeakMap, WeakSet, parseInt, parseFloat, isNaN, isFinite, undefined, btoa: (s) => Buffer.from(s, "binary").toString("base64"), atob: (s) => Buffer.from(s, "base64").toString("binary"), }; vm.createContext(ctx); ctx.global = ctx; ctx.window = ctx; // UMD 把 Jose 挂到第一参 window 上 -> 挂到 ctx ctx.self = ctx; try { vm.runInContext(src, ctx, { filename: "encrypt.js", timeout: 30000 }); } catch (e) { console.log("[run-err]", e.message); } console.log("webpackJsonp len=", (ctx.webpackJsonp || []).length); // 取出 088e factory let factory = null; try { const pushed = ctx.webpackJsonp[0]; // [["encrypt"], { "088e": factory }] factory = pushed[1]["088e"]; console.log("got 088e factory:", typeof factory); } catch (e) { console.log("[extract-err]", e.message); } if (typeof factory === "function") { // stub require const requireStub = (id) => { if (id === "b639") return { Buffer }; if (id === "dd40") return (x) => x; // identity wrapper return {}; // core-js polyfills -> no-op }; const mod = { exports: {} }; try { factory(mod, mod.exports, requireStub); console.log("[factory] ran ok. window.Jose type =", typeof ctx.Jose); } catch (e) { console.log("[factory-err]", e.message, "\n", e.stack.split("\n").slice(0, 6).join("\n")); } const Jose = ctx.Jose; if (Jose) { console.log("Jose type:", typeof Jose, "keys:", Object.keys(Jose)); const j = (typeof Jose === "function") ? new Jose() : Jose; console.log("instance proto:", Object.getOwnPropertyNames(Object.getPrototypeOf(j))); const rg = j.realm && j.realm.global; if (rg) { console.log("realm.global keys:", Object.keys(rg)); console.log("$encrypt type:", typeof rg["$encrypt"], " $decrypt type:", typeof rg["$decrypt"]); console.log("has String?", typeof rg.String, " Math?", typeof rg.Math, " JSON?", typeof rg.JSON); } // 尝试把宿主 builtins 灌进 realm.global if (rg) { for (const k of ["String","Number","Boolean","Array","Object","Math","JSON","Date","parseInt","parseFloat","encodeURIComponent","decodeURIComponent","Error","RegExp","Map","Set","Uint8Array","ArrayBuffer","DataView"]) { try { if (rg[k] === undefined) rg[k] = ctx[k]; } catch (_) {} } console.log("after seed: String?", typeof rg.String); } // 实测 $encrypt const data = new Uint8Array([123, 34, 97, 34, 58, 49, 125]); // {"a":1} const key = "c7b645db-65e8-401f-b38c-4c07c5fff247"; let out, errInfo; const p = new Promise((res) => { try { j.call("$encrypt", [data, key, { suc: (v) => { out = v; res(); }, err: (e) => { errInfo = e; res(); } }]); } catch (e) { errInfo = e; res(); } }); Promise.race([p, new Promise((r) => setTimeout(() => r(), 4000))]).then(() => { if (errInfo) console.log("[encrypt-err]", errInfo && errInfo.message ? errInfo.message : errInfo); if (out) { const b64 = Buffer.from(out).toString("base64"); console.log("verifyParam(b64) len=", b64.length, " head=", b64.slice(0, 80)); } else { console.log("[no output]"); } }); } }