specific-ai-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +177 -0
- package/README.ja.md +177 -0
- package/README.ko.md +177 -0
- package/README.md +174 -2
- package/README.zh-TW.md +177 -0
- package/assets/banner.txt +3 -0
- package/dist/cli/argv.js +98 -0
- package/dist/cli/commands/auth.js +11 -0
- package/dist/cli/commands/config.js +23 -0
- package/dist/cli/commands/doctor.js +134 -0
- package/dist/cli/commands/platforms.js +24 -0
- package/dist/cli/commands/version.js +4 -0
- package/dist/cli/confirm.js +22 -0
- package/dist/cli/dispatch.js +440 -0
- package/dist/cli/help.js +223 -0
- package/dist/cli/hints.js +60 -0
- package/dist/cli/main.js +18 -0
- package/dist/cli/output.js +66 -0
- package/dist/core/auth-handlers.js +41 -0
- package/dist/core/auth-store.js +119 -0
- package/dist/core/capabilities.js +163 -0
- package/dist/core/config.js +121 -0
- package/dist/core/cookies.js +164 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/files.js +89 -0
- package/dist/core/fsutil.js +52 -0
- package/dist/core/hash.js +4 -0
- package/dist/core/http.js +331 -0
- package/dist/core/image.js +280 -0
- package/dist/core/log.js +40 -0
- package/dist/core/login.js +191 -0
- package/dist/core/mp4.js +130 -0
- package/dist/core/normalize.js +207 -0
- package/dist/core/options.js +110 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/pb.js +59 -0
- package/dist/core/py.js +181 -0
- package/dist/core/rand.js +131 -0
- package/dist/core/registry.js +129 -0
- package/dist/core/schemas.js +41 -0
- package/dist/core/stream.js +104 -0
- package/dist/core/toolkit.js +35 -0
- package/dist/core/vm.js +73 -0
- package/dist/core/vocab.js +157 -0
- package/dist/platforms/_shared/impaas.js +410 -0
- package/dist/platforms/bilibili/index.js +216 -0
- package/dist/platforms/bilibili/web/api.js +806 -0
- package/dist/platforms/bilibili/web/client.js +311 -0
- package/dist/platforms/bilibili/web/commands.js +783 -0
- package/dist/platforms/bilibili/web/ddddocr.js +342 -0
- package/dist/platforms/bilibili/web/gaia.js +130 -0
- package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
- package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
- package/dist/platforms/bilibili/web/geetest.js +450 -0
- package/dist/platforms/bilibili/web/normalize.js +234 -0
- package/dist/platforms/bilibili/web/profile.js +135 -0
- package/dist/platforms/bilibili/web/resolve.js +112 -0
- package/dist/platforms/bilibili/web/sign.js +223 -0
- package/dist/platforms/bilibili/web/upos.js +102 -0
- package/dist/platforms/douyin/index.js +142 -0
- package/dist/platforms/douyin/web/api.js +863 -0
- package/dist/platforms/douyin/web/client.js +413 -0
- package/dist/platforms/douyin/web/commands.js +712 -0
- package/dist/platforms/douyin/web/creator.js +601 -0
- package/dist/platforms/douyin/web/crypto.js +247 -0
- package/dist/platforms/douyin/web/dtrait.js +190 -0
- package/dist/platforms/douyin/web/im.js +502 -0
- package/dist/platforms/douyin/web/image.js +5 -0
- package/dist/platforms/douyin/web/live.js +225 -0
- package/dist/platforms/douyin/web/mssdk.js +148 -0
- package/dist/platforms/douyin/web/normalize.js +326 -0
- package/dist/platforms/douyin/web/passport.js +1247 -0
- package/dist/platforms/douyin/web/profile.js +221 -0
- package/dist/platforms/douyin/web/proto.js +26 -0
- package/dist/platforms/douyin/web/resolve.js +88 -0
- package/dist/platforms/douyin/web/sign.js +436 -0
- package/dist/platforms/douyin/web/tos.js +78 -0
- package/dist/platforms/index.js +17 -0
- package/dist/platforms/jd/index.js +67 -0
- package/dist/platforms/jd/web/aks.js +115 -0
- package/dist/platforms/jd/web/api.js +549 -0
- package/dist/platforms/jd/web/chat.js +132 -0
- package/dist/platforms/jd/web/client.js +470 -0
- package/dist/platforms/jd/web/commands.js +557 -0
- package/dist/platforms/jd/web/jcap/helper.js +90 -0
- package/dist/platforms/jd/web/jcap/host.js +107 -0
- package/dist/platforms/jd/web/jcap/lsd.js +300 -0
- package/dist/platforms/jd/web/jcap/solver.js +995 -0
- package/dist/platforms/jd/web/jcap.js +150 -0
- package/dist/platforms/jd/web/js.js +420 -0
- package/dist/platforms/jd/web/login.js +775 -0
- package/dist/platforms/jd/web/normalize.js +297 -0
- package/dist/platforms/jd/web/profile.js +256 -0
- package/dist/platforms/jd/web/util.js +175 -0
- package/dist/platforms/jd/web/webm.js +133 -0
- package/dist/platforms/kuaishou/index.js +98 -0
- package/dist/platforms/kuaishou/web/api.js +694 -0
- package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
- package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
- package/dist/platforms/kuaishou/web/captcha.js +273 -0
- package/dist/platforms/kuaishou/web/client.js +487 -0
- package/dist/platforms/kuaishou/web/commands.js +863 -0
- package/dist/platforms/kuaishou/web/gap.js +144 -0
- package/dist/platforms/kuaishou/web/gdfp.js +376 -0
- package/dist/platforms/kuaishou/web/gql.js +11 -0
- package/dist/platforms/kuaishou/web/live.js +125 -0
- package/dist/platforms/kuaishou/web/normalize.js +241 -0
- package/dist/platforms/kuaishou/web/oracle.js +329 -0
- package/dist/platforms/kuaishou/web/profile.js +259 -0
- package/dist/platforms/kuaishou/web/resolve.js +84 -0
- package/dist/platforms/kuaishou/web/session.js +592 -0
- package/dist/platforms/kuaishou/web/sign.js +568 -0
- package/dist/platforms/taobao/index.js +56 -0
- package/dist/platforms/taobao/web/api.js +96 -0
- package/dist/platforms/taobao/web/client.js +102 -0
- package/dist/platforms/taobao/web/commands.js +172 -0
- package/dist/platforms/taobao/web/im.js +68 -0
- package/dist/platforms/taobao/web/normalize.js +103 -0
- package/dist/platforms/taobao/web/profile.js +85 -0
- package/dist/platforms/taobao/web/resolve.js +51 -0
- package/dist/platforms/taobao/web/sign.js +35 -0
- package/dist/platforms/tiktok/index.js +120 -0
- package/dist/platforms/tiktok/web/api.js +1234 -0
- package/dist/platforms/tiktok/web/client.js +362 -0
- package/dist/platforms/tiktok/web/commands.js +821 -0
- package/dist/platforms/tiktok/web/im.js +133 -0
- package/dist/platforms/tiktok/web/jsrun.js +126 -0
- package/dist/platforms/tiktok/web/normalize.js +286 -0
- package/dist/platforms/tiktok/web/profile.js +169 -0
- package/dist/platforms/tiktok/web/resolve.js +156 -0
- package/dist/platforms/tiktok/web/sign.js +585 -0
- package/dist/platforms/tiktok/web/upload.js +805 -0
- package/dist/platforms/tiktok/web/wire.js +490 -0
- package/dist/platforms/weibo/index.js +91 -0
- package/dist/platforms/weibo/web/api.js +206 -0
- package/dist/platforms/weibo/web/client.js +155 -0
- package/dist/platforms/weibo/web/commands.js +202 -0
- package/dist/platforms/weibo/web/normalize.js +137 -0
- package/dist/platforms/weibo/web/profile.js +246 -0
- package/dist/platforms/weibo/web/resolve.js +60 -0
- package/dist/platforms/weibo/web/sign.js +85 -0
- package/dist/platforms/x/index.js +107 -0
- package/dist/platforms/x/web/api.js +443 -0
- package/dist/platforms/x/web/article.js +212 -0
- package/dist/platforms/x/web/client.js +202 -0
- package/dist/platforms/x/web/commands.js +363 -0
- package/dist/platforms/x/web/graphql.js +108 -0
- package/dist/platforms/x/web/normalize.js +235 -0
- package/dist/platforms/x/web/profile.js +99 -0
- package/dist/platforms/x/web/resolve.js +44 -0
- package/dist/platforms/x/web/transaction.js +171 -0
- package/dist/platforms/xhs/index.js +101 -0
- package/dist/platforms/xhs/web/api.js +278 -0
- package/dist/platforms/xhs/web/client.js +540 -0
- package/dist/platforms/xhs/web/commands.js +1046 -0
- package/dist/platforms/xhs/web/creator-api.js +609 -0
- package/dist/platforms/xhs/web/creator.js +203 -0
- package/dist/platforms/xhs/web/js.js +194 -0
- package/dist/platforms/xhs/web/login.js +230 -0
- package/dist/platforms/xhs/web/normalize.js +166 -0
- package/dist/platforms/xhs/web/profile.js +127 -0
- package/dist/platforms/xhs/web/push.js +361 -0
- package/dist/platforms/xhs/web/resolve.js +69 -0
- package/dist/platforms/xhs/web/state.js +368 -0
- package/dist/platforms/xianyu/index.js +59 -0
- package/dist/platforms/xianyu/web/api.js +312 -0
- package/dist/platforms/xianyu/web/client.js +134 -0
- package/dist/platforms/xianyu/web/commands.js +305 -0
- package/dist/platforms/xianyu/web/im.js +73 -0
- package/dist/platforms/xianyu/web/normalize.js +123 -0
- package/dist/platforms/xianyu/web/profile.js +146 -0
- package/dist/platforms/xianyu/web/resolve.js +48 -0
- package/dist/platforms/xianyu/web/sign.js +79 -0
- package/package.json +77 -4
- package/static/douyin/Live.proto +194 -0
- package/static/douyin/Request.proto +89 -0
- package/static/douyin/Response.proto +46 -0
- package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
- package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
- package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
- package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
- package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
- package/static/douyin/challenge_profile.json +801 -0
- package/static/douyin/challenge_template_runner.js +217 -0
- package/static/douyin/dtrait_profile.json +66 -0
- package/static/douyin/mstoken_common_profile.json +1 -0
- package/static/douyin/mstoken_profile.json +214 -0
- package/static/jd/h5st5_env.js +750 -0
- package/static/jd/h5st5_lib.js +5 -0
- package/static/jd/h5st5_server.js +49 -0
- package/static/jd/jcap/env/env_core.js +512 -0
- package/static/jd/jcap/env/run.js +1307 -0
- package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
- package/static/jd/package.json +4 -0
- package/static/jd/pc_tk_lib.js +1 -0
- package/static/jd/pc_tk_server.js +63 -0
- package/static/jd/summer_cryptico_runner.js +50 -0
- package/static/jd/webm/env/run.js +453 -0
- package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
- package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
- package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
- package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
- package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
- package/static/kuaishou/js/cp-kwf.js +1 -0
- package/static/kuaishou/tools/kwf_oracle.js +799 -0
- package/static/kuaishou/tools/kws_oracle.js +885 -0
- package/static/kuaishou/tools/like_token_oracle.js +70 -0
- package/static/taobao/taobao_js_20260407.js +570 -0
- package/static/tiktok/Tiktok_Request.json +597 -0
- package/static/tiktok/package.json +3 -0
- package/static/tiktok/shop_bsid/core.js +1 -0
- package/static/tiktok/shop_bsid/env_core.js +512 -0
- package/static/tiktok/shop_bsid/loader.js +1 -0
- package/static/tiktok/shop_bsid/run.js +753 -0
- package/static/tiktok/shop_bsid/sign.js +100 -0
- package/static/tiktok/shop_bsid/webmssdk.js +1 -0
- package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/env/env_core.js +512 -0
- package/static/tiktok/signing/env/sign.js +455 -0
- package/static/tiktok/signing/sdk_config.json +230 -0
- package/static/tiktok/signing/source/webmssdk.js +1 -0
- package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
- package/static/x/graphql.json +7777 -0
- package/static/x/transaction_l1.json +12 -0
- package/static/xhs/package.json +3 -0
- package/static/xhs/xhs_core/js/b1.js +210 -0
- package/static/xhs/xhs_core/js/mns.js +340 -0
- package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
- package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
- package/static/xhs/xhs_core/js/sign.js +225 -0
- package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
- package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
- package/static/xhs/xhs_core/js/xs_common.js +79 -0
- package/static/xhs/xhs_creator/js/profile.js +115 -0
- package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
- package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
- package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
- package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
- package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
- package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
- package/static/xhs/xhs_pc/js/b1.js +9 -0
- package/static/xhs/xhs_pc/js/deflate.js +320 -0
- package/static/xhs/xhs_pc/js/mns.js +4 -0
- package/static/xhs/xhs_pc/js/profile.js +141 -0
- package/static/xhs/xhs_pc/js/rap.js +173 -0
- package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
- package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
- package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
- package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
- package/static/xhs/xhs_pc/js/sign.js +6 -0
- package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
- package/static/xhs/xhs_pc/js/xs_common.js +4 -0
- package/static/xianyu/et_f.js +2 -0
- package/static/xianyu/gen_tfstk.js +410 -0
- package/static/xianyu/goofish_js_version_2.js +568 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kws 预言机:在 Node 里跑真实的 kws 脚本,拿到 window.kwscb(code) 交出的 kwscode。
|
|
3
|
+
*
|
|
4
|
+
* 背景(utils/sign/webweapon_boot.py 的 handleSignature):
|
|
5
|
+
*
|
|
6
|
+
* window.kwscb = t => { setCookie("kwssectoken", secToken, {expires: 6/1440});
|
|
7
|
+
* setCookie("kwscode", t, {expires: 6/1440}); };
|
|
8
|
+
* loadScript(cfg.signUrl); // kws 脚本跑完回调 kwscb 交出 kwscode
|
|
9
|
+
*
|
|
10
|
+
* 所以 kwscode **不是**服务端下发的,是 kws 脚本本地算的 —— 和 kwfv1 同理。
|
|
11
|
+
* 换票 /s/w/c 每次随机发不同变体(kws-3/11/13/15/16),同一套 Brook 引擎、
|
|
12
|
+
* 不同混淆种子,本预言机对五个变体通用。
|
|
13
|
+
*
|
|
14
|
+
* 用法:
|
|
15
|
+
* node reverse/tools/kws_oracle.js 用默认环境跑 kws-13
|
|
16
|
+
* node reverse/tools/kws_oracle.js <script.js> 指定变体
|
|
17
|
+
* node reverse/tools/kws_oracle.js <script.js> --json 出 JSON(给 Python 调)
|
|
18
|
+
* node reverse/tools/kws_oracle.js <script.js> --trace 打印读过的环境属性
|
|
19
|
+
*
|
|
20
|
+
* 环境参数走环境变量,便于 Python 侧注入真实会话值:
|
|
21
|
+
* KS_DID / KS_PRODUCT / KS_HREF / KS_COOKIE / KS_SECTOKEN
|
|
22
|
+
*/
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const vm = require('vm');
|
|
26
|
+
|
|
27
|
+
const WEAPON_DIR = path.join(__dirname, '..', 'bundles', 'weapon');
|
|
28
|
+
const DEFAULT_SCRIPT = 'kws-13-0.0.1-obfuscated.6b74e9640ff18648.js';
|
|
29
|
+
|
|
30
|
+
// 标签名 -> DOM 接口名。真实浏览器里
|
|
31
|
+
// Object.prototype.toString.call(document.createElement("div"))
|
|
32
|
+
// 返回 "[object HTMLDivElement]",脚本用它验元素是不是真的。
|
|
33
|
+
const ELEMENT_INTERFACE = {
|
|
34
|
+
div: 'HTMLDivElement',
|
|
35
|
+
a: 'HTMLAnchorElement',
|
|
36
|
+
p: 'HTMLParagraphElement',
|
|
37
|
+
h1: 'HTMLHeadingElement',
|
|
38
|
+
h2: 'HTMLHeadingElement',
|
|
39
|
+
h3: 'HTMLHeadingElement',
|
|
40
|
+
h4: 'HTMLHeadingElement',
|
|
41
|
+
h5: 'HTMLHeadingElement',
|
|
42
|
+
h6: 'HTMLHeadingElement',
|
|
43
|
+
span: 'HTMLSpanElement',
|
|
44
|
+
ul: 'HTMLUListElement',
|
|
45
|
+
ol: 'HTMLOListElement',
|
|
46
|
+
li: 'HTMLLIElement',
|
|
47
|
+
iframe: 'HTMLIFrameElement',
|
|
48
|
+
canvas: 'HTMLCanvasElement',
|
|
49
|
+
script: 'HTMLScriptElement',
|
|
50
|
+
img: 'HTMLImageElement',
|
|
51
|
+
input: 'HTMLInputElement',
|
|
52
|
+
form: 'HTMLFormElement',
|
|
53
|
+
table: 'HTMLTableElement',
|
|
54
|
+
body: 'HTMLBodyElement',
|
|
55
|
+
head: 'HTMLHeadElement',
|
|
56
|
+
style: 'HTMLStyleElement',
|
|
57
|
+
link: 'HTMLLinkElement',
|
|
58
|
+
button: 'HTMLButtonElement',
|
|
59
|
+
select: 'HTMLSelectElement',
|
|
60
|
+
textarea: 'HTMLTextAreaElement',
|
|
61
|
+
video: 'HTMLVideoElement',
|
|
62
|
+
audio: 'HTMLAudioElement',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
|
|
66
|
+
+ '(KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 造一个够 kws 跑的浏览器环境。
|
|
70
|
+
*
|
|
71
|
+
* 采集面与 kwf 基本重合(同一套 SDK),所以直接沿用 kwf_env 摸出来的那套:
|
|
72
|
+
* navigator 的 language/languages/webdriver/pdfViewerEnabled/onLine/platform/
|
|
73
|
+
* vendor/productSub/appName/doNotTrack/cookieEnabled,加 document.cookie、
|
|
74
|
+
* documentElement 上的 webdriver 标记探测、createElement("iframe") 的
|
|
75
|
+
* contentWindow,以及 location.hostname。
|
|
76
|
+
*/
|
|
77
|
+
function buildEnv(opts) {
|
|
78
|
+
const o = Object.assign({
|
|
79
|
+
language: 'zh-CN',
|
|
80
|
+
languages: ['zh-CN', 'zh', 'en', 'zh-TW', 'ja'],
|
|
81
|
+
pdfViewerEnabled: true,
|
|
82
|
+
onLine: true,
|
|
83
|
+
webdriver: false,
|
|
84
|
+
cookie: process.env.KS_COOKIE || '',
|
|
85
|
+
hostname: 'www.kuaishou.com',
|
|
86
|
+
href: process.env.KS_HREF || 'https://www.kuaishou.com/new-reco',
|
|
87
|
+
secToken: process.env.KS_SECTOKEN || '',
|
|
88
|
+
scriptUrl: process.env.KS_KWS_SCRIPT_URL || '',
|
|
89
|
+
trace: false,
|
|
90
|
+
// Browser contracts proven against three character-for-character Chrome
|
|
91
|
+
// oracles are enabled by default. A few switches remain only for
|
|
92
|
+
// reversible negative-control diagnostics.
|
|
93
|
+
elementTypeTag: process.env.KS_KWS_ELEMENT_TAG !== '0',
|
|
94
|
+
nativeToStringPatch: process.env.KS_KWS_NATIVE_TOSTRING !== '0',
|
|
95
|
+
pcTrace: process.env.KS_KWS_TRACE_PC === '1',
|
|
96
|
+
builtinTrace: process.env.KS_KWS_TRACE_BUILTINS === '1',
|
|
97
|
+
propertyTrace: process.env.KS_KWS_TRACE_PROPERTIES === '1',
|
|
98
|
+
functionTrace: process.env.KS_KWS_TRACE_FUNCTIONS === '1',
|
|
99
|
+
navigatorPrototypeChain: process.env.KS_KWS_NAVIGATOR_PROTO !== '0',
|
|
100
|
+
documentPrototypeChain: process.env.KS_KWS_DOCUMENT_PROTO === '1'
|
|
101
|
+
|| process.env.KS_KWS_BROWSER_PROTOS === '1',
|
|
102
|
+
documentCookieOnPrototype: process.env.KS_KWS_COOKIE_PROTO === '1',
|
|
103
|
+
documentCreateElementOnPrototype: process.env.KS_KWS_CREATE_ELEMENT_PROTO === '1',
|
|
104
|
+
navigatorWritable: process.env.KS_KWS_NAVIGATOR_WRITABLE === '1',
|
|
105
|
+
elementPrototypeChains: process.env.KS_KWS_ELEMENT_PROTOS === '1',
|
|
106
|
+
elementInterfaces: process.env.KS_KWS_ELEMENT_INTERFACES === '1',
|
|
107
|
+
domPropertiesOnPrototype: process.env.KS_KWS_DOM_PROTO_PROPS === '1',
|
|
108
|
+
layoutAttachedHeight: process.env.KS_KWS_LAYOUT_HEIGHT !== '0',
|
|
109
|
+
webdriverMarker: process.env.KS_KWS_WEBDRIVER_MARKER === '1',
|
|
110
|
+
domHierarchyErrors: process.env.KS_KWS_DOM_HIERARCHY !== '0',
|
|
111
|
+
domExceptionInterface: process.env.KS_KWS_DOM_EXCEPTION === '1',
|
|
112
|
+
navigatorInterfaces: process.env.KS_KWS_NAVIGATOR_INTERFACES === '1',
|
|
113
|
+
navigatorDirectObjectPrototype: process.env.KS_KWS_NAVIGATOR_DIRECT_OBJECT === '1',
|
|
114
|
+
locationPrototypeChain: process.env.KS_KWS_LOCATION_PROTO === '1',
|
|
115
|
+
windowPrototypeChain: process.env.KS_KWS_WINDOW_PROTO !== '0',
|
|
116
|
+
windowPropertiesPrototype: process.env.KS_KWS_WINDOW_PROPERTIES === '1',
|
|
117
|
+
windowAccessorProperties: process.env.KS_KWS_WINDOW_ACCESSORS === '1',
|
|
118
|
+
vmUriIntrinsics: process.env.KS_KWS_VM_URI_INTRINSICS === '1',
|
|
119
|
+
}, opts || {});
|
|
120
|
+
|
|
121
|
+
const touched = [];
|
|
122
|
+
let currentPc = null;
|
|
123
|
+
let nextElementId = 1;
|
|
124
|
+
const elementIds = new WeakMap();
|
|
125
|
+
const record = (entry) => {
|
|
126
|
+
touched.push(o.pcTrace && currentPc !== null ? `pc=${currentPc} ${entry}` : entry);
|
|
127
|
+
};
|
|
128
|
+
const describe = (value) => {
|
|
129
|
+
if (value === null) return 'null';
|
|
130
|
+
if (value === undefined) return 'undefined';
|
|
131
|
+
if (typeof value === 'object') {
|
|
132
|
+
return value.tagName
|
|
133
|
+
? `<${value.tagName}#${elementIds.get(value) || '?'}>` : '{object}';
|
|
134
|
+
}
|
|
135
|
+
return `${typeof value}:${String(value).slice(0, 40)}`;
|
|
136
|
+
};
|
|
137
|
+
const store = {};
|
|
138
|
+
const hostLabels = new WeakMap();
|
|
139
|
+
const browserInterfaces = {};
|
|
140
|
+
if (o.domExceptionInterface && typeof DOMException === 'function') {
|
|
141
|
+
browserInterfaces.DOMException = DOMException;
|
|
142
|
+
}
|
|
143
|
+
const interfacePrototypes = {};
|
|
144
|
+
const elementTags = new WeakMap();
|
|
145
|
+
const ensureElementInterfaces = () => {
|
|
146
|
+
if (interfacePrototypes.HTMLElement) return;
|
|
147
|
+
const defineInterface = (name, parentName) => {
|
|
148
|
+
const parentProto = parentName ? interfacePrototypes[parentName] : Object.prototype;
|
|
149
|
+
const proto = Object.create(parentProto);
|
|
150
|
+
const Ctor = { [name]: function () {} }[name];
|
|
151
|
+
Object.defineProperty(proto, 'constructor', {
|
|
152
|
+
value: Ctor, writable: true, configurable: true,
|
|
153
|
+
});
|
|
154
|
+
Object.defineProperty(proto, Symbol.toStringTag, {
|
|
155
|
+
value: name, configurable: true,
|
|
156
|
+
});
|
|
157
|
+
Ctor.prototype = proto;
|
|
158
|
+
interfacePrototypes[name] = proto;
|
|
159
|
+
browserInterfaces[name] = Ctor;
|
|
160
|
+
};
|
|
161
|
+
defineInterface('EventTarget');
|
|
162
|
+
defineInterface('Node', 'EventTarget');
|
|
163
|
+
defineInterface('Element', 'Node');
|
|
164
|
+
defineInterface('HTMLElement', 'Element');
|
|
165
|
+
if (o.domPropertiesOnPrototype) {
|
|
166
|
+
Object.defineProperty(interfacePrototypes.Node, 'nodeName', {
|
|
167
|
+
get() { return String(elementTags.get(this) || '').toUpperCase(); },
|
|
168
|
+
enumerable: true, configurable: true,
|
|
169
|
+
});
|
|
170
|
+
Object.defineProperty(interfacePrototypes.Element, 'tagName', {
|
|
171
|
+
get() { return String(elementTags.get(this) || '').toUpperCase(); },
|
|
172
|
+
enumerable: true, configurable: true,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
for (const name of new Set(Object.values(ELEMENT_INTERFACE).concat('HTMLUnknownElement'))) {
|
|
176
|
+
defineInterface(name, 'HTMLElement');
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// WeakSet:追踪已被 append 进 DOM 的元素(body/head.appendChild 都记)
|
|
181
|
+
const attached = new WeakSet();
|
|
182
|
+
const parentNode = new WeakMap();
|
|
183
|
+
const childNodes = new WeakMap();
|
|
184
|
+
const appendNode = (parent, child, connected) => {
|
|
185
|
+
if (!child || typeof child !== 'object') {
|
|
186
|
+
throw new TypeError("Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.");
|
|
187
|
+
}
|
|
188
|
+
if (o.domHierarchyErrors) {
|
|
189
|
+
for (let cursor = parent; cursor; cursor = parentNode.get(cursor)) {
|
|
190
|
+
if (cursor === child) {
|
|
191
|
+
throw new DOMException(
|
|
192
|
+
"Failed to execute 'appendChild' on 'Node': The new child element contains the parent.",
|
|
193
|
+
'HierarchyRequestError');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const oldParent = parentNode.get(child);
|
|
198
|
+
if (oldParent) {
|
|
199
|
+
const oldChildren = childNodes.get(oldParent) || [];
|
|
200
|
+
const oldIndex = oldChildren.indexOf(child);
|
|
201
|
+
if (oldIndex >= 0) oldChildren.splice(oldIndex, 1);
|
|
202
|
+
}
|
|
203
|
+
parentNode.set(child, parent);
|
|
204
|
+
const children = childNodes.get(parent) || [];
|
|
205
|
+
children.push(child);
|
|
206
|
+
childNodes.set(parent, children);
|
|
207
|
+
if (connected) attached.add(child);
|
|
208
|
+
return child;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// ------------------------------------------------------------------ //
|
|
212
|
+
// navigator 必须是**只读**的 //
|
|
213
|
+
// ------------------------------------------------------------------ //
|
|
214
|
+
// kws 的 Brook VM 有一个 canary 注入检测:它往 navigator.platform /
|
|
215
|
+
// appCodeName / userAgent 里写一个标记串(实测是 "ctrip.com"),再读回来
|
|
216
|
+
// 比对。真实浏览器里这些都是 Navigator.prototype 上的 **getter-only**
|
|
217
|
+
// 访问器,非严格模式下赋值静默失败,读回来仍是原值;
|
|
218
|
+
// 而普通对象字面量会被真的写进去 —— VM 一看值变了就判定环境被 hook,
|
|
219
|
+
// 转去走另一条代码路径,产出的 kwscode 里会出现 nibble > 15
|
|
220
|
+
// (表现为 g/h/i 这种非 hex 字符,浏览器真实值是纯 hex)。
|
|
221
|
+
//
|
|
222
|
+
// 所以这里用 defineProperty 定义成只有 getter 的属性,
|
|
223
|
+
// 复刻浏览器「可读、写不进去」的语义。
|
|
224
|
+
const navigator = {};
|
|
225
|
+
hostLabels.set(navigator, 'navigator');
|
|
226
|
+
const navigatorTarget = o.navigatorPrototypeChain ? {} : navigator;
|
|
227
|
+
const NAV_VALUES = {
|
|
228
|
+
userAgent: UA,
|
|
229
|
+
appVersion: UA.slice('Mozilla/'.length),
|
|
230
|
+
platform: 'Win32',
|
|
231
|
+
language: o.language,
|
|
232
|
+
languages: o.languages,
|
|
233
|
+
vendor: 'Google Inc.',
|
|
234
|
+
hardwareConcurrency: 20,
|
|
235
|
+
deviceMemory: 8,
|
|
236
|
+
maxTouchPoints: 0,
|
|
237
|
+
webdriver: o.webdriver,
|
|
238
|
+
pdfViewerEnabled: o.pdfViewerEnabled,
|
|
239
|
+
cookieEnabled: true,
|
|
240
|
+
doNotTrack: null,
|
|
241
|
+
onLine: o.onLine,
|
|
242
|
+
plugins: { length: 5 },
|
|
243
|
+
mimeTypes: { length: 2 },
|
|
244
|
+
product: 'Gecko',
|
|
245
|
+
productSub: '20030107',
|
|
246
|
+
appName: 'Netscape',
|
|
247
|
+
appCodeName: 'Mozilla',
|
|
248
|
+
};
|
|
249
|
+
// Chrome 的只读字段位于 Navigator.prototype,实例本身没有这些数据属性。
|
|
250
|
+
// KWS 同时观察可写语义和原型身份;因此正式环境使用完整
|
|
251
|
+
// navigator -> Navigator.prototype -> EventTarget.prototype -> Object.prototype
|
|
252
|
+
// 链,并在 Navigator.prototype 上保留 getter-only/configurable:true 描述符。
|
|
253
|
+
for (const [key, value] of Object.entries(NAV_VALUES)) {
|
|
254
|
+
Object.defineProperty(navigatorTarget, key, o.navigatorWritable ? {
|
|
255
|
+
value,
|
|
256
|
+
writable: true,
|
|
257
|
+
enumerable: true,
|
|
258
|
+
configurable: true,
|
|
259
|
+
} : {
|
|
260
|
+
get() { record('navigator.' + key); return value; },
|
|
261
|
+
// 只给 getter、不给 setter:真实浏览器里这些就是 getter-only 访问器,
|
|
262
|
+
// 非严格模式下赋值静默失败。加空 setter 会被识破(异常计数器多涨一处)。
|
|
263
|
+
enumerable: true,
|
|
264
|
+
// configurable 必须是 true —— CDP 实测 Navigator.prototype 的 platform
|
|
265
|
+
// 描述符就是 {get: fn, configurable: true}。
|
|
266
|
+
configurable: true,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (o.navigatorPrototypeChain) {
|
|
270
|
+
const eventTargetProto = Object.create(Object.prototype);
|
|
271
|
+
const navigatorProto = Object.create(
|
|
272
|
+
o.navigatorDirectObjectPrototype ? Object.prototype : eventTargetProto);
|
|
273
|
+
if (o.navigatorInterfaces) {
|
|
274
|
+
const EventTarget = function EventTarget() {};
|
|
275
|
+
const Navigator = function Navigator() {};
|
|
276
|
+
Object.defineProperty(eventTargetProto, 'constructor', {
|
|
277
|
+
value: EventTarget, writable: true, configurable: true,
|
|
278
|
+
});
|
|
279
|
+
Object.defineProperty(navigatorProto, 'constructor', {
|
|
280
|
+
value: Navigator, writable: true, configurable: true,
|
|
281
|
+
});
|
|
282
|
+
EventTarget.prototype = eventTargetProto;
|
|
283
|
+
Navigator.prototype = navigatorProto;
|
|
284
|
+
browserInterfaces.EventTarget = EventTarget;
|
|
285
|
+
browserInterfaces.Navigator = Navigator;
|
|
286
|
+
}
|
|
287
|
+
for (const key of Object.getOwnPropertyNames(navigatorTarget)) {
|
|
288
|
+
Object.defineProperty(navigatorProto, key,
|
|
289
|
+
Object.getOwnPropertyDescriptor(navigatorTarget, key));
|
|
290
|
+
}
|
|
291
|
+
Object.defineProperty(navigatorProto, Symbol.toStringTag, {
|
|
292
|
+
value: 'Navigator', configurable: true,
|
|
293
|
+
});
|
|
294
|
+
Object.setPrototypeOf(navigator, navigatorProto);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let cookieJar = o.cookie;
|
|
298
|
+
const locationObj = {
|
|
299
|
+
href: o.href,
|
|
300
|
+
origin: 'https://' + o.hostname,
|
|
301
|
+
protocol: 'https:',
|
|
302
|
+
host: o.hostname,
|
|
303
|
+
hostname: o.hostname,
|
|
304
|
+
pathname: (() => { try { return new URL(o.href).pathname; } catch (e) { return '/'; } })(),
|
|
305
|
+
search: '',
|
|
306
|
+
hash: '',
|
|
307
|
+
};
|
|
308
|
+
hostLabels.set(locationObj, 'location');
|
|
309
|
+
if (o.locationPrototypeChain) {
|
|
310
|
+
const locationProto = Object.create(Object.prototype);
|
|
311
|
+
const Location = function Location() {};
|
|
312
|
+
Object.defineProperty(locationProto, 'constructor', {
|
|
313
|
+
value: Location, writable: true, configurable: true,
|
|
314
|
+
});
|
|
315
|
+
Object.defineProperty(locationProto, Symbol.toStringTag, {
|
|
316
|
+
value: 'Location', configurable: true,
|
|
317
|
+
});
|
|
318
|
+
Location.prototype = locationProto;
|
|
319
|
+
Object.setPrototypeOf(locationObj, locationProto);
|
|
320
|
+
browserInterfaces.Location = Location;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const documentObj = {
|
|
324
|
+
get cookie() { record('document.cookie'); return cookieJar; },
|
|
325
|
+
set cookie(v) { record('document.cookie=' + String(v).slice(0, 60)); cookieJar = v; },
|
|
326
|
+
referrer: '',
|
|
327
|
+
title: '快手',
|
|
328
|
+
characterSet: 'UTF-8',
|
|
329
|
+
documentElement: {
|
|
330
|
+
clientWidth: 1920, clientHeight: 947, lang: 'zh-CN',
|
|
331
|
+
getAttribute(name) { record(`documentElement.getAttribute(${describe(name)})`); return null; },
|
|
332
|
+
hasAttribute(name) { record(`documentElement.hasAttribute(${describe(name)})`); return false; },
|
|
333
|
+
},
|
|
334
|
+
body: {
|
|
335
|
+
clientWidth: 1920, clientHeight: 947,
|
|
336
|
+
appendChild(child) {
|
|
337
|
+
record(`body.appendChild(${describe(child)})`);
|
|
338
|
+
return appendNode(documentObj.body, child, true);
|
|
339
|
+
},
|
|
340
|
+
removeChild(child) { record(`body.removeChild(${describe(child)})`); attached.delete(child); return child; },
|
|
341
|
+
},
|
|
342
|
+
head: {
|
|
343
|
+
appendChild(child) { attached.add(child); return child; },
|
|
344
|
+
removeChild(child) { attached.delete(child); return child; },
|
|
345
|
+
},
|
|
346
|
+
scripts: { length: 27 },
|
|
347
|
+
createElement(tag) {
|
|
348
|
+
record('createElement:' + tag);
|
|
349
|
+
if (tag === 'iframe') {
|
|
350
|
+
// 与 kwf 同样的反 hook 比对:需要一个可用的 contentWindow
|
|
351
|
+
const inner = {};
|
|
352
|
+
inner.window = inner;
|
|
353
|
+
inner.self = inner;
|
|
354
|
+
inner.navigator = navigator;
|
|
355
|
+
inner.document = documentObj;
|
|
356
|
+
inner.location = locationObj;
|
|
357
|
+
return {
|
|
358
|
+
style: {}, setAttribute() {}, appendChild() {}, remove() {},
|
|
359
|
+
get contentWindow() { return inner; },
|
|
360
|
+
get contentDocument() { return documentObj; },
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
// ---------------------------------------------------------------- //
|
|
364
|
+
// div canary(所有 kws 变体都做) //
|
|
365
|
+
// ---------------------------------------------------------------- //
|
|
366
|
+
// 脚本的动作是:createElement("div") -> style.height = "20px"
|
|
367
|
+
// -> document.body.appendChild(div) -> 读 offsetHeight
|
|
368
|
+
// -> div.remove()
|
|
369
|
+
// 真实浏览器实测(CDP 在 www.kuaishou.com 上取的权威值):
|
|
370
|
+
// 未挂载时 offsetHeight = 0;挂到 body 上之后 = 20;remove 后又变 0。
|
|
371
|
+
// 之前恒返回 0,VM 认为「设了高度却量不到」= 环境是假的,
|
|
372
|
+
// 于是走异常分支,产出的 kwscode 出现 nibble > 15 的非 hex 字符。
|
|
373
|
+
const style = {};
|
|
374
|
+
const el = {
|
|
375
|
+
style,
|
|
376
|
+
className: '',
|
|
377
|
+
id: '',
|
|
378
|
+
innerHTML: '',
|
|
379
|
+
get parentNode() { record(`el<${tag}>.parentNode`); return parentNode.get(el) || null; },
|
|
380
|
+
get parentElement() { record(`el<${tag}>.parentElement`); return parentNode.get(el) || null; },
|
|
381
|
+
get childNodes() { record(`el<${tag}>.childNodes`); return childNodes.get(el) || []; },
|
|
382
|
+
get children() { record(`el<${tag}>.children`); return childNodes.get(el) || []; },
|
|
383
|
+
get firstChild() { return (childNodes.get(el) || [])[0] || null; },
|
|
384
|
+
get lastChild() {
|
|
385
|
+
const children = childNodes.get(el) || [];
|
|
386
|
+
return children.length ? children[children.length - 1] : null;
|
|
387
|
+
},
|
|
388
|
+
get childElementCount() { return (childNodes.get(el) || []).length; },
|
|
389
|
+
get offsetHeight() {
|
|
390
|
+
const value = o.layoutAttachedHeight && attached.has(el)
|
|
391
|
+
? parseInt(style.height, 10) || 0 : 0;
|
|
392
|
+
record(`el<${tag}>.offsetHeight=${value}`);
|
|
393
|
+
return value;
|
|
394
|
+
},
|
|
395
|
+
get clientHeight() { return attached.has(el) ? parseInt(style.height, 10) || 0 : 0; },
|
|
396
|
+
get offsetWidth() { return attached.has(el) ? parseInt(style.width, 10) || 0 : 0; },
|
|
397
|
+
get clientWidth() { return attached.has(el) ? parseInt(style.width, 10) || 0 : 0; },
|
|
398
|
+
setAttribute() {}, getAttribute() { return null; },
|
|
399
|
+
hasAttribute() { return false; },
|
|
400
|
+
appendChild(child) {
|
|
401
|
+
record(`el<${tag}#${elementIds.get(el) || '?'}>.appendChild(${describe(child)})`);
|
|
402
|
+
return appendNode(el, child, attached.has(el));
|
|
403
|
+
},
|
|
404
|
+
removeChild(child) {
|
|
405
|
+
record(`el<${tag}#${elementIds.get(el) || '?'}>.removeChild(${describe(child)})`);
|
|
406
|
+
attached.delete(child); return child;
|
|
407
|
+
},
|
|
408
|
+
remove() {
|
|
409
|
+
record(`el<${tag}#${elementIds.get(el) || '?'}>.remove`); attached.delete(el);
|
|
410
|
+
},
|
|
411
|
+
contains(other) {
|
|
412
|
+
record(`el<${tag}>.contains(${describe(other)})`);
|
|
413
|
+
for (let cursor = other; cursor; cursor = parentNode.get(cursor)) {
|
|
414
|
+
if (cursor === el) return true;
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
},
|
|
418
|
+
getContext() { return null; },
|
|
419
|
+
toDataURL() { return 'data:image/png;base64,'; },
|
|
420
|
+
getBoundingClientRect() {
|
|
421
|
+
const h = attached.has(el) ? parseInt(style.height, 10) || 0 : 0;
|
|
422
|
+
const w = attached.has(el) ? parseInt(style.width, 10) || 0 : 0;
|
|
423
|
+
return { width: w, height: h, top: 0, left: 0, right: w, bottom: h };
|
|
424
|
+
},
|
|
425
|
+
addEventListener() {},
|
|
426
|
+
};
|
|
427
|
+
hostLabels.set(el, `el<${tag}#${nextElementId}>`);
|
|
428
|
+
if (!o.domPropertiesOnPrototype) {
|
|
429
|
+
el.tagName = String(tag).toUpperCase();
|
|
430
|
+
el.nodeName = String(tag).toUpperCase();
|
|
431
|
+
}
|
|
432
|
+
elementTags.set(el, String(tag));
|
|
433
|
+
elementIds.set(el, nextElementId++);
|
|
434
|
+
// Object.prototype.toString.call(el) 在真实浏览器里是
|
|
435
|
+
// "[object HTMLDivElement]" 这类**具体接口名**,普通对象字面量只会给
|
|
436
|
+
// "[object Object]"。脚本批量建 div/a/p/h1..h4/span/ul/li 再读 tagName,
|
|
437
|
+
// 就是在验这个。Symbol.toStringTag 可以让 Object.prototype.toString
|
|
438
|
+
// 返回指定的名字。
|
|
439
|
+
if (o.elementTypeTag) {
|
|
440
|
+
Object.defineProperty(el, Symbol.toStringTag, {
|
|
441
|
+
get() { return ELEMENT_INTERFACE[String(tag).toLowerCase()] || 'HTMLUnknownElement'; },
|
|
442
|
+
configurable: true,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (o.elementInterfaces) {
|
|
446
|
+
ensureElementInterfaces();
|
|
447
|
+
const interfaceName = ELEMENT_INTERFACE[String(tag).toLowerCase()] || 'HTMLUnknownElement';
|
|
448
|
+
Object.setPrototypeOf(el, interfacePrototypes[interfaceName]);
|
|
449
|
+
} else if (o.elementPrototypeChains) {
|
|
450
|
+
const eventTargetProto = Object.create(Object.prototype);
|
|
451
|
+
const nodeProto = Object.create(eventTargetProto);
|
|
452
|
+
const elementProto = Object.create(nodeProto);
|
|
453
|
+
const htmlElementProto = Object.create(elementProto);
|
|
454
|
+
Object.setPrototypeOf(el, htmlElementProto);
|
|
455
|
+
}
|
|
456
|
+
return el;
|
|
457
|
+
},
|
|
458
|
+
getElementsByTagName() { return { length: 0 }; },
|
|
459
|
+
querySelector() { return null; },
|
|
460
|
+
addEventListener() {},
|
|
461
|
+
};
|
|
462
|
+
hostLabels.set(documentObj, 'document');
|
|
463
|
+
hostLabels.set(documentObj.documentElement, 'documentElement');
|
|
464
|
+
hostLabels.set(documentObj.body, 'body');
|
|
465
|
+
hostLabels.set(documentObj.head, 'head');
|
|
466
|
+
if (o.webdriverMarker) documentObj.__webdriver_evaluate = true;
|
|
467
|
+
if (o.documentCookieOnPrototype) {
|
|
468
|
+
const cookieDescriptor = Object.getOwnPropertyDescriptor(documentObj, 'cookie');
|
|
469
|
+
const cookieProto = Object.create(Object.getPrototypeOf(documentObj));
|
|
470
|
+
Object.defineProperty(cookieProto, 'cookie', cookieDescriptor);
|
|
471
|
+
delete documentObj.cookie;
|
|
472
|
+
Object.setPrototypeOf(documentObj, cookieProto);
|
|
473
|
+
}
|
|
474
|
+
if (o.documentCreateElementOnPrototype) {
|
|
475
|
+
const createElementDescriptor = Object.getOwnPropertyDescriptor(documentObj, 'createElement');
|
|
476
|
+
const documentMethodProto = Object.create(Object.getPrototypeOf(documentObj));
|
|
477
|
+
Object.defineProperty(documentMethodProto, 'createElement', createElementDescriptor);
|
|
478
|
+
delete documentObj.createElement;
|
|
479
|
+
Object.setPrototypeOf(documentObj, documentMethodProto);
|
|
480
|
+
}
|
|
481
|
+
if (o.documentPrototypeChain) {
|
|
482
|
+
const eventTargetProto = Object.create(Object.prototype);
|
|
483
|
+
const nodeProto = Object.create(eventTargetProto);
|
|
484
|
+
const documentProto = Object.create(nodeProto);
|
|
485
|
+
Object.defineProperty(documentProto, Symbol.toStringTag, {
|
|
486
|
+
value: 'HTMLDocument', configurable: true,
|
|
487
|
+
});
|
|
488
|
+
Object.setPrototypeOf(documentObj, documentProto);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (o.elementInterfaces) ensureElementInterfaces();
|
|
492
|
+
const sandbox = {};
|
|
493
|
+
hostLabels.set(sandbox, 'window');
|
|
494
|
+
if (o.windowPrototypeChain) {
|
|
495
|
+
const eventTargetProto = Object.create(Object.prototype);
|
|
496
|
+
const windowPropertiesProto = o.windowPropertiesPrototype
|
|
497
|
+
? Object.create(eventTargetProto) : eventTargetProto;
|
|
498
|
+
const windowProto = Object.create(windowPropertiesProto);
|
|
499
|
+
const EventTarget = browserInterfaces.EventTarget || function EventTarget() {};
|
|
500
|
+
const Window = function Window() {};
|
|
501
|
+
Object.defineProperty(eventTargetProto, 'constructor', {
|
|
502
|
+
value: EventTarget, writable: true, configurable: true,
|
|
503
|
+
});
|
|
504
|
+
Object.defineProperty(windowProto, 'constructor', {
|
|
505
|
+
value: Window, writable: true, configurable: true,
|
|
506
|
+
});
|
|
507
|
+
Object.defineProperty(windowProto, Symbol.toStringTag, {
|
|
508
|
+
value: 'Window', configurable: true,
|
|
509
|
+
});
|
|
510
|
+
EventTarget.prototype = eventTargetProto;
|
|
511
|
+
Window.prototype = windowProto;
|
|
512
|
+
Object.setPrototypeOf(sandbox, windowProto);
|
|
513
|
+
browserInterfaces.EventTarget = EventTarget;
|
|
514
|
+
browserInterfaces.Window = Window;
|
|
515
|
+
}
|
|
516
|
+
if (o.windowAccessorProperties) {
|
|
517
|
+
for (const key of ['window', 'self', 'globalThis', 'top', 'parent']) {
|
|
518
|
+
Object.defineProperty(sandbox, key, {
|
|
519
|
+
get() { return sandbox; }, enumerable: true, configurable: true,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
Object.defineProperties(sandbox, {
|
|
523
|
+
navigator: {
|
|
524
|
+
get() { return navigator; }, enumerable: true, configurable: true,
|
|
525
|
+
},
|
|
526
|
+
document: {
|
|
527
|
+
get() { return documentObj; }, enumerable: true, configurable: true,
|
|
528
|
+
},
|
|
529
|
+
location: {
|
|
530
|
+
get() { return locationObj; }, set() {}, enumerable: true, configurable: true,
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
} else {
|
|
534
|
+
sandbox.window = sandbox;
|
|
535
|
+
sandbox.self = sandbox;
|
|
536
|
+
sandbox.globalThis = sandbox;
|
|
537
|
+
sandbox.top = sandbox;
|
|
538
|
+
sandbox.parent = sandbox;
|
|
539
|
+
sandbox.navigator = navigator;
|
|
540
|
+
sandbox.document = documentObj;
|
|
541
|
+
sandbox.location = locationObj;
|
|
542
|
+
}
|
|
543
|
+
Object.assign(sandbox, browserInterfaces);
|
|
544
|
+
sandbox.screen = {
|
|
545
|
+
width: 1920, height: 1080, availWidth: 1920, availHeight: 1032,
|
|
546
|
+
colorDepth: 24, pixelDepth: 24, availLeft: 0, availTop: 0,
|
|
547
|
+
};
|
|
548
|
+
sandbox.localStorage = {
|
|
549
|
+
getItem(k) { record('ls.get:' + k); return k in store ? store[k] : null; },
|
|
550
|
+
setItem(k, v) { record('ls.set:' + k); store[k] = String(v); },
|
|
551
|
+
removeItem(k) { delete store[k]; },
|
|
552
|
+
};
|
|
553
|
+
sandbox.sessionStorage = sandbox.localStorage;
|
|
554
|
+
sandbox.console = { log() {}, warn() {}, error() {}, debug() {}, info() {} };
|
|
555
|
+
sandbox.performance = { now: () => Date.now() % 100000, timeOrigin: Date.now() };
|
|
556
|
+
sandbox.chrome = { runtime: {} };
|
|
557
|
+
sandbox.devicePixelRatio = 1;
|
|
558
|
+
sandbox.innerWidth = 1920;
|
|
559
|
+
sandbox.innerHeight = 947;
|
|
560
|
+
sandbox.outerWidth = 1920;
|
|
561
|
+
sandbox.outerHeight = 1032;
|
|
562
|
+
sandbox.screenX = 0;
|
|
563
|
+
sandbox.screenY = 0;
|
|
564
|
+
sandbox.setTimeout = (fn) => { if (typeof fn === 'function') { try { fn(); } catch (e) {} } return 0; };
|
|
565
|
+
sandbox.setInterval = () => 0;
|
|
566
|
+
sandbox.clearTimeout = () => {};
|
|
567
|
+
sandbox.clearInterval = () => {};
|
|
568
|
+
sandbox.addEventListener = () => {};
|
|
569
|
+
sandbox.removeEventListener = () => {};
|
|
570
|
+
sandbox.atob = (s) => Buffer.from(s, 'base64').toString('binary');
|
|
571
|
+
sandbox.btoa = (s) => Buffer.from(s, 'binary').toString('base64');
|
|
572
|
+
if (!o.vmUriIntrinsics) {
|
|
573
|
+
sandbox.encodeURI = encodeURI;
|
|
574
|
+
sandbox.encodeURIComponent = encodeURIComponent;
|
|
575
|
+
sandbox.decodeURIComponent = decodeURIComponent;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// 关键:SDK 在加载 kws 脚本前就挂好 kwscb,脚本算完回调它交出 kwscode
|
|
579
|
+
const captured = { kwscode: null, calls: [], pcAtCall: null, pcTailAtCall: [] };
|
|
580
|
+
const pcHistory = [];
|
|
581
|
+
const functionIds = new WeakMap();
|
|
582
|
+
const functionStack = [];
|
|
583
|
+
let nextFunctionId = 1;
|
|
584
|
+
sandbox.__kwsStep = function (pc, instruction) {
|
|
585
|
+
if (!o.pcTrace) return;
|
|
586
|
+
currentPc = pc;
|
|
587
|
+
pcHistory.push({ pc, instruction: Array.from(instruction || []) });
|
|
588
|
+
if (pcHistory.length > 256) pcHistory.shift();
|
|
589
|
+
};
|
|
590
|
+
sandbox.__kwsHostLabel = function (value) {
|
|
591
|
+
return value && (typeof value === 'object' || typeof value === 'function')
|
|
592
|
+
? hostLabels.get(value) || '' : '';
|
|
593
|
+
};
|
|
594
|
+
sandbox.__kwsBuiltin = function (name, label, detail) {
|
|
595
|
+
if (o.builtinTrace && label) record(`builtin ${name} ${label} ${detail || ''}`.trim());
|
|
596
|
+
};
|
|
597
|
+
sandbox.__kwsPropertyRead = function (base, key, value) {
|
|
598
|
+
if (!o.propertyTrace) return value;
|
|
599
|
+
let label = base && (typeof base === 'object' || typeof base === 'function')
|
|
600
|
+
? hostLabels.get(base) || '' : '';
|
|
601
|
+
// vm contextifies the global object, so the value received back by a host
|
|
602
|
+
// callback is not guaranteed to retain the original sandbox identity.
|
|
603
|
+
if (!label && base && typeof base === 'object') {
|
|
604
|
+
try {
|
|
605
|
+
if (base.document === documentObj && base.navigator === navigator) label = 'window';
|
|
606
|
+
} catch (e) { /* not the context global */ }
|
|
607
|
+
}
|
|
608
|
+
if (label) {
|
|
609
|
+
record(`property ${label}.${String(key)} -> ${describe(value)}`);
|
|
610
|
+
if (value && (typeof value === 'object' || typeof value === 'function')
|
|
611
|
+
&& !hostLabels.has(value)) {
|
|
612
|
+
hostLabels.set(value, `${label}.${String(key)}`);
|
|
613
|
+
}
|
|
614
|
+
} else if (String(key) === 'stack') {
|
|
615
|
+
record(`property <error>.stack -> ${String(value).replace(/\s+/g, ' ').slice(0, 500)}`);
|
|
616
|
+
}
|
|
617
|
+
return value;
|
|
618
|
+
};
|
|
619
|
+
sandbox.__kwsFunctionEnter = function (program, args) {
|
|
620
|
+
if (!o.functionTrace) return;
|
|
621
|
+
let id = functionIds.get(program);
|
|
622
|
+
if (!id) {
|
|
623
|
+
id = nextFunctionId++;
|
|
624
|
+
functionIds.set(program, id);
|
|
625
|
+
}
|
|
626
|
+
functionStack.push(id);
|
|
627
|
+
let argsText = '';
|
|
628
|
+
try { argsText = ` args=${JSON.stringify(Array.from(args || []))}`; } catch (e) {}
|
|
629
|
+
record(`fn-enter ${id} len=${program.length} first=${JSON.stringify(program[0])}`
|
|
630
|
+
+ ` last=${JSON.stringify(program[program.length - 1])}${argsText}`);
|
|
631
|
+
};
|
|
632
|
+
sandbox.__kwsFunctionExit = function (program, value) {
|
|
633
|
+
const id = functionIds.get(program) || '?';
|
|
634
|
+
if (o.functionTrace) {
|
|
635
|
+
record(`fn-exit ${id} -> ${describe(value)}`);
|
|
636
|
+
functionStack.pop();
|
|
637
|
+
}
|
|
638
|
+
return value;
|
|
639
|
+
};
|
|
640
|
+
sandbox.kwscb = function (code) {
|
|
641
|
+
captured.calls.push(code);
|
|
642
|
+
if (captured.kwscode === null) captured.kwscode = code;
|
|
643
|
+
if (o.pcTrace && captured.pcAtCall === null) {
|
|
644
|
+
captured.pcAtCall = pcHistory.length ? pcHistory[pcHistory.length - 1].pc : null;
|
|
645
|
+
captured.pcTailAtCall = pcHistory.slice(-48);
|
|
646
|
+
}
|
|
647
|
+
record('kwscb(' + String(code).slice(0, 24) + '…)');
|
|
648
|
+
return true;
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
if (typeof o.mutateEnv === 'function') {
|
|
652
|
+
o.mutateEnv({ sandbox, navigator, document: documentObj, location: locationObj });
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return { sandbox, store, touched, captured, pcHistory, options: o,
|
|
656
|
+
getCookie: () => cookieJar };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** 跑一次 kws 脚本,返回 kwscode。 */
|
|
660
|
+
function run(scriptFile, opts) {
|
|
661
|
+
const env = buildEnv(opts);
|
|
662
|
+
const file = path.isAbsolute(scriptFile) ? scriptFile : path.join(WEAPON_DIR, scriptFile);
|
|
663
|
+
let src = fs.readFileSync(file, 'utf8');
|
|
664
|
+
if (env.options.functionTrace) {
|
|
665
|
+
const factory = src.match(/var ([\w$]+) = [\w$]+\.slice\([^;]+?\),[\w$]+ = [\w$]+;[\w$]+\(function \(\) \{[\s\S]*?\};([\w$]+)\(\1\);[\s\S]*?return ([\w$]+)\(([\w$]+)\[0\]\);\},/);
|
|
666
|
+
if (!factory) throw new Error('未能定位 Brook 函数工厂');
|
|
667
|
+
const [, program, execute, dereferenceValue, returnSlots] = factory;
|
|
668
|
+
const executeNeedle = `;${execute}(${program});`;
|
|
669
|
+
const returnNeedle = `return ${dereferenceValue}(${returnSlots}[0]);`;
|
|
670
|
+
src = src.replace(executeNeedle,
|
|
671
|
+
`;__kwsFunctionEnter(${program},arguments);${execute}(${program});`);
|
|
672
|
+
src = src.replace(returnNeedle,
|
|
673
|
+
`var __kwsRet=${dereferenceValue}(${returnSlots}[0]);`
|
|
674
|
+
+ `__kwsRet=__kwsFunctionExit(${program},__kwsRet);return __kwsRet;`);
|
|
675
|
+
if (!src.includes('__kwsFunctionEnter(') || !src.includes('__kwsFunctionExit(')) {
|
|
676
|
+
throw new Error('未能注入 Brook 函数调用诊断钩子');
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (env.options.propertyTrace) {
|
|
680
|
+
// Every Brook value is represented as either a direct value or an
|
|
681
|
+
// object/key reference. Patch the single reference dereference helper so
|
|
682
|
+
// direct VM property reads from window/document/navigator/DOM are visible;
|
|
683
|
+
// wrapping Object.* alone misses these accesses entirely.
|
|
684
|
+
const dereference = /var ([\w$]+) = function \(([\w$]+)\) \{return \2\.([\w$]+) \? \2\.([\w$]+)\[\2\.([\w$]+)\] : \2\.([\w$]+);\};/;
|
|
685
|
+
src = src.replace(dereference, (all, fn, ref, isReference, base, key, direct) =>
|
|
686
|
+
`var ${fn} = function (${ref}) {return ${ref}.${isReference} ? `
|
|
687
|
+
+ `__kwsPropertyRead(${ref}.${base}, ${ref}.${key}, ${ref}.${base}[${ref}.${key}]) `
|
|
688
|
+
+ `: ${ref}.${direct};};`);
|
|
689
|
+
if (!src.includes('__kwsPropertyRead(')) {
|
|
690
|
+
throw new Error('未能注入 Brook 属性读取诊断钩子');
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (env.options.pcTrace) {
|
|
694
|
+
// Both Brook execution loops have the same compact shape despite their
|
|
695
|
+
// randomized identifier prefixes. Inject a read-only PC tap immediately
|
|
696
|
+
// before each opcode dispatch; the captured official script itself stays
|
|
697
|
+
// immutable on disk.
|
|
698
|
+
const dispatch = /(var ([\w$]+) = ([\w$]+)\[([\w$]+)\[([\w$]+)\]\];)([\w$]+) = \2\(/g;
|
|
699
|
+
src = src.replace(dispatch, (all, declaration, handler, table, instruction,
|
|
700
|
+
zero, pc) => `${declaration}try { __kwsStep(${pc}, ${instruction}); } catch (_) {} ${pc} = ${handler}(`);
|
|
701
|
+
}
|
|
702
|
+
const ctx = vm.createContext(env.sandbox);
|
|
703
|
+
|
|
704
|
+
// 把 sandbox 里所有函数收集进一个 WeakSet,供下面的 toString 补丁识别。
|
|
705
|
+
// 递归遍历(含 getter 取到的对象),深度够覆盖 document.body.appendChild 这种。
|
|
706
|
+
vm.runInContext('globalThis.__stubFns = new WeakSet();', ctx);
|
|
707
|
+
const stubSet = env.sandbox.__stubFns;
|
|
708
|
+
const seen = new Set();
|
|
709
|
+
(function collect(obj, depth) {
|
|
710
|
+
if (!obj || depth > 4) return;
|
|
711
|
+
if (typeof obj !== 'object' && typeof obj !== 'function') return;
|
|
712
|
+
if (seen.has(obj)) return;
|
|
713
|
+
seen.add(obj);
|
|
714
|
+
let keys = [];
|
|
715
|
+
try { keys = Object.getOwnPropertyNames(obj); } catch (e) { return; }
|
|
716
|
+
for (const key of keys) {
|
|
717
|
+
try {
|
|
718
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
|
|
719
|
+
if (descriptor) {
|
|
720
|
+
if (typeof descriptor.get === 'function') stubSet.add(descriptor.get);
|
|
721
|
+
if (typeof descriptor.set === 'function') stubSet.add(descriptor.set);
|
|
722
|
+
}
|
|
723
|
+
} catch (e) { /* host descriptor lookup can throw */ }
|
|
724
|
+
let value;
|
|
725
|
+
try { value = obj[key]; } catch (e) { continue; } // getter 可能抛
|
|
726
|
+
if (typeof value === 'function') {
|
|
727
|
+
try { stubSet.add(value); } catch (e) { /* 跨 realm 的忽略 */ }
|
|
728
|
+
collect(value, depth + 1);
|
|
729
|
+
} else if (value && typeof value === 'object') {
|
|
730
|
+
collect(value, depth + 1);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
})(env.sandbox, 0);
|
|
734
|
+
|
|
735
|
+
// ------------------------------------------------------------------ //
|
|
736
|
+
// 让所有桩函数伪装成原生实现 //
|
|
737
|
+
// ------------------------------------------------------------------ //
|
|
738
|
+
// 真实浏览器里 document.createElement / appendChild / getAttribute 这些
|
|
739
|
+
// 都是宿主对象方法,Function.prototype.toString 会给
|
|
740
|
+
// "function createElement() { [native code] }"。
|
|
741
|
+
// 我们的桩是普通 JS 函数,toString 会把源码原样吐出来 —— 脚本一验就穿。
|
|
742
|
+
//
|
|
743
|
+
// 注意 **必须在 vm 内部打这个补丁**:vm.createContext 有自己独立的
|
|
744
|
+
// intrinsics,宿主 realm 的 Function.prototype 补丁在里面完全不生效
|
|
745
|
+
// (实测 vm 的 Function.prototype.toString !== 宿主的)。
|
|
746
|
+
//
|
|
747
|
+
// 判据用 WeakSet 白名单:只有我们造的桩才伪装成原生,
|
|
748
|
+
// kws 脚本自己的函数仍返回真实源码(浏览器里也是这样)。
|
|
749
|
+
if (env.options.nativeToStringPatch) {
|
|
750
|
+
vm.runInContext(`
|
|
751
|
+
(function () {
|
|
752
|
+
var origToString = Function.prototype.toString;
|
|
753
|
+
var isStub = globalThis.__stubFns; // 由宿主塞进来的 WeakSet
|
|
754
|
+
function fakeToString() {
|
|
755
|
+
if (isStub && isStub.has(this)) {
|
|
756
|
+
return 'function ' + (this.name || '') + '() { [native code] }';
|
|
757
|
+
}
|
|
758
|
+
return origToString.call(this);
|
|
759
|
+
}
|
|
760
|
+
Function.prototype.toString = fakeToString;
|
|
761
|
+
// toString 自己也得像原生的,否则一验 toString.toString() 就露馅
|
|
762
|
+
isStub.add(fakeToString);
|
|
763
|
+
isStub.add(Function.prototype.toString);
|
|
764
|
+
delete globalThis.__stubFns;
|
|
765
|
+
})();
|
|
766
|
+
`, ctx);
|
|
767
|
+
}
|
|
768
|
+
if (env.options.builtinTrace) {
|
|
769
|
+
vm.runInContext(`
|
|
770
|
+
(function () {
|
|
771
|
+
var label = globalThis.__kwsHostLabel;
|
|
772
|
+
var report = globalThis.__kwsBuiltin;
|
|
773
|
+
var gp = Object.getPrototypeOf;
|
|
774
|
+
Object.getPrototypeOf = function (value) {
|
|
775
|
+
var out = gp(value); var l = label(value);
|
|
776
|
+
if (l) report('getPrototypeOf', l, label(out) || Object.prototype.toString.call(out));
|
|
777
|
+
return out;
|
|
778
|
+
};
|
|
779
|
+
var gd = Object.getOwnPropertyDescriptor;
|
|
780
|
+
Object.getOwnPropertyDescriptor = function (value, key) {
|
|
781
|
+
var out = gd(value, key); var l = label(value);
|
|
782
|
+
if (l) report('getOwnPropertyDescriptor', l, String(key) + ':' + (out ? Object.keys(out).join(',') : 'missing'));
|
|
783
|
+
return out;
|
|
784
|
+
};
|
|
785
|
+
var gn = Object.getOwnPropertyNames;
|
|
786
|
+
Object.getOwnPropertyNames = function (value) {
|
|
787
|
+
var out = gn(value); var l = label(value);
|
|
788
|
+
if (l) report('getOwnPropertyNames', l, out.join(','));
|
|
789
|
+
return out;
|
|
790
|
+
};
|
|
791
|
+
var hop = Object.prototype.hasOwnProperty;
|
|
792
|
+
Object.prototype.hasOwnProperty = function (key) {
|
|
793
|
+
var l = label(this); var out = hop.call(this, key);
|
|
794
|
+
if (l) report('hasOwnProperty', l, String(key) + ':' + out);
|
|
795
|
+
return out;
|
|
796
|
+
};
|
|
797
|
+
var ots = Object.prototype.toString;
|
|
798
|
+
Object.prototype.toString = function () {
|
|
799
|
+
var out = ots.call(this); var l = label(this);
|
|
800
|
+
if (l) report('toString', l, out);
|
|
801
|
+
return out;
|
|
802
|
+
};
|
|
803
|
+
})();
|
|
804
|
+
`, ctx);
|
|
805
|
+
}
|
|
806
|
+
// A browser compiles an external KWS bundle with its HTTPS script URL as the
|
|
807
|
+
// source name. Leaving Node's default ``evalmachine.<anonymous>`` leaks the
|
|
808
|
+
// vm host through Error.stack; the official payload explicitly checks for
|
|
809
|
+
// that marker. The exact signUrl is supplied by Python in production.
|
|
810
|
+
const sourceName = env.options.scriptUrl
|
|
811
|
+
|| `https://static.yximgs.com/${path.basename(file)}`;
|
|
812
|
+
vm.runInContext(src, ctx, { timeout: 30000, filename: sourceName });
|
|
813
|
+
|
|
814
|
+
// 有的变体是异步交付(挂在 setTimeout 里),我们的 setTimeout 是同步跑的,
|
|
815
|
+
// 所以这里通常已经拿到了。若还没有,再尝试主动触发常见入口。
|
|
816
|
+
if (env.captured.kwscode === null) {
|
|
817
|
+
for (const name of ['kwpsec', 'kws', 'kwsign']) {
|
|
818
|
+
const obj = env.sandbox[name];
|
|
819
|
+
if (obj && typeof obj.getData === 'function') {
|
|
820
|
+
try { env.captured.kwscode = obj.getData(); } catch (e) {}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return env;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function main() {
|
|
828
|
+
const args = process.argv.slice(2);
|
|
829
|
+
const flags = args.filter((a) => a.startsWith('--'));
|
|
830
|
+
const script = args.find((a) => !a.startsWith('--')) || DEFAULT_SCRIPT;
|
|
831
|
+
const asJson = flags.includes('--json');
|
|
832
|
+
const trace = flags.includes('--trace');
|
|
833
|
+
const pcTrace = flags.includes('--pc-trace');
|
|
834
|
+
const builtinTrace = flags.includes('--builtin-trace');
|
|
835
|
+
const propertyTrace = flags.includes('--property-trace');
|
|
836
|
+
const functionTrace = flags.includes('--function-trace');
|
|
837
|
+
|
|
838
|
+
let env;
|
|
839
|
+
try {
|
|
840
|
+
env = run(script, { trace, pcTrace, builtinTrace, propertyTrace, functionTrace });
|
|
841
|
+
} catch (e) {
|
|
842
|
+
if (asJson) {
|
|
843
|
+
console.log(JSON.stringify({ ok: false, error: String(e && e.message || e) }));
|
|
844
|
+
process.exit(1);
|
|
845
|
+
}
|
|
846
|
+
console.error('跑 kws 失败:', e && e.message || e);
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const code = env.captured.kwscode;
|
|
851
|
+
if (asJson) {
|
|
852
|
+
console.log(JSON.stringify({
|
|
853
|
+
ok: code != null,
|
|
854
|
+
script,
|
|
855
|
+
kwscode: code,
|
|
856
|
+
length: code == null ? 0 : String(code).length,
|
|
857
|
+
calls: env.captured.calls.length,
|
|
858
|
+
cookie: env.getCookie(),
|
|
859
|
+
pcHistory: pcTrace ? env.pcHistory : undefined,
|
|
860
|
+
pcAtCall: pcTrace ? env.captured.pcAtCall : undefined,
|
|
861
|
+
pcTailAtCall: pcTrace ? env.captured.pcTailAtCall : undefined,
|
|
862
|
+
}));
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
console.log('脚本 :', script);
|
|
867
|
+
console.log('kwscode:', code);
|
|
868
|
+
console.log('长度 :', code == null ? 0 : String(code).length);
|
|
869
|
+
console.log('回调次数:', env.captured.calls.length);
|
|
870
|
+
const ck = env.getCookie();
|
|
871
|
+
if (ck) console.log('脚本写的 cookie:', String(ck).slice(0, 160));
|
|
872
|
+
if (trace) {
|
|
873
|
+
console.log('\n=== 读过的环境属性(去重连续项)===');
|
|
874
|
+
let prev = null; let n = 0;
|
|
875
|
+
for (const t of env.touched) {
|
|
876
|
+
if (t === prev) continue;
|
|
877
|
+
prev = t;
|
|
878
|
+
console.log(' ' + String(++n).padStart(3) + ' ' + t);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (require.main === module) main();
|
|
884
|
+
|
|
885
|
+
module.exports = { run, buildEnv, WEAPON_DIR, DEFAULT_SCRIPT };
|