86 lines
3.5 KiB
JavaScript
86 lines
3.5 KiB
JavaScript
// 纯算加密服务:调用快手 captcha 的 Jose VM 生成 verifyParam。
|
||
//
|
||
// 加密管线(从 iframe bundle d702 模块还原):
|
||
// verifyParam = base64( Jose.$encrypt( utf8(JSON.stringify(payload)), KEY ) )
|
||
// KEY = "c7b645db-65e8-401f-b38c-4c07c5fff247" (硬编码, iframe bundle d702)
|
||
// Jose = encrypt.js 的字节码 VM; $encrypt 内含随机 IV.
|
||
//
|
||
// 用法 (CLI):
|
||
// node tools/jose_encrypt.js '{"captchaSn":"...","relativeX":123,...}'
|
||
// -> stdout: verifyParam(base64)
|
||
//
|
||
// 关键: 沙箱必须是【单 realm】——不能注入外层 String/Object/Uint8Array 等,
|
||
// 否则 VM 对宿主基本类型字符串的 .charCodeAt 方法派发失败 (跨 realm 原型链查不到)。
|
||
|
||
const fs = require("fs"), vm = require("vm"), path = require("path");
|
||
|
||
const ENC_PATH = path.join(__dirname, "..", "out", "captcha_net",
|
||
"js_07_rm_static_captcha_js_encrypt_ee7d2a41_js.js");
|
||
const KEY = "c7b645db-65e8-401f-b38c-4c07c5fff247";
|
||
|
||
function makeBox() {
|
||
const sandbox = {
|
||
console: { log: () => {}, error: () => {}, warn: () => {} },
|
||
setTimeout, clearTimeout, setImmediate, queueMicrotask,
|
||
Buffer,
|
||
btoa: (s) => Buffer.from(s, "binary").toString("base64"),
|
||
atob: (s) => Buffer.from(s, "base64").toString("binary"),
|
||
};
|
||
vm.createContext(sandbox); // 让 context 自带 intrinsic
|
||
sandbox.window = sandbox; sandbox.self = sandbox; sandbox.global = sandbox;
|
||
const src = fs.readFileSync(ENC_PATH, "utf-8");
|
||
vm.runInContext(src, sandbox, { filename: "encrypt.js", timeout: 30000 });
|
||
const factory = sandbox.webpackJsonp[0][1]["088e"];
|
||
const rq = (id) => id === "b639" ? { Buffer } : id === "dd40" ? (x) => x : {};
|
||
const mod = { exports: {} };
|
||
factory(mod, mod.exports, rq);
|
||
if (!sandbox.Jose) throw new Error("Jose 未加载");
|
||
return sandbox;
|
||
}
|
||
|
||
// 预置 in-sandbox 辅助 + 加密入口(全单 realm)
|
||
function primeBox(sandbox) {
|
||
vm.runInContext(`
|
||
globalThis.__enc = function(payloadStr){
|
||
var u8 = new Uint8Array(Array.prototype.map.call(payloadStr, function(c){return c.charCodeAt(0);}));
|
||
globalThis.__out=null; globalThis.__done=null;
|
||
Jose.call("$encrypt", [u8, ${JSON.stringify(KEY)}, {
|
||
suc: function(v){ var s=""; for(var i=0;i<v.length;i++) s+=String.fromCharCode(v[i]); globalThis.__out=btoa(s); globalThis.__done='ok'; },
|
||
err: function(e){ globalThis.__out='ERR:'+(e&&e.message||e); globalThis.__done='err'; }
|
||
}]);
|
||
};
|
||
`, sandbox);
|
||
}
|
||
|
||
function encrypt(sandbox, payloadObj) {
|
||
const json = JSON.stringify(payloadObj);
|
||
sandbox.__done = null; sandbox.__out = null;
|
||
vm.runInContext(`__enc(${JSON.stringify(json)});`, sandbox);
|
||
// VM 回调经 setTimeout 异步, 外部轮询沙箱全局标志
|
||
return new Promise((resolve) => {
|
||
const t0 = Date.now();
|
||
const iv = setInterval(() => {
|
||
if (sandbox.__done !== null || Date.now() - t0 > 5000) {
|
||
clearInterval(iv);
|
||
resolve(sandbox.__done === "ok" ? sandbox.__out : (sandbox.__out || "ERR:timeout"));
|
||
}
|
||
}, 5);
|
||
});
|
||
}
|
||
|
||
module.exports = { makeBox, primeBox, encrypt, KEY };
|
||
|
||
// ---- CLI ----
|
||
if (require.main === module) {
|
||
const arg = process.argv[2];
|
||
if (!arg) { console.error("usage: node jose_encrypt.js '<json-payload>'"); process.exit(2); }
|
||
const box = makeBox(); primeBox(box);
|
||
let payload;
|
||
try { payload = JSON.parse(arg); }
|
||
catch (e) { console.error("payload JSON parse 失败:", e.message); process.exit(2); }
|
||
encrypt(box, payload).then((out) => {
|
||
if (out && out.indexOf("ERR:") === 0) { console.error(out); process.exit(1); }
|
||
process.stdout.write(out || "");
|
||
});
|
||
}
|