deepspider 0.2.0 → 0.2.7

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.
@@ -380,41 +380,327 @@ function getWebSocketHooks() {
380
380
 
381
381
  /**
382
382
  * Webpack 模块 Hook - 检测闭包内的加密库
383
+ * 通过特征检测而非变量名来识别加密库
383
384
  */
384
385
  function getWebpackHooks() {
385
386
  return `
386
- // === Webpack Hook ===
387
+ // === Webpack Module Hook ===
387
388
  (function() {
388
389
  const deepspider = window.__deepspider__;
389
390
  if (!deepspider) return;
390
391
 
391
- // 加密特征检测
392
- const cryptoPatterns = [
393
- { name: 'CryptoJS', pattern: /CryptoJS|crypto-js/i },
394
- { name: 'MD5', pattern: /\\bmd5\\b/i },
395
- { name: 'SHA', pattern: /\\bsha(1|256|512)\\b/i },
396
- { name: 'AES', pattern: /\\baes\\b/i },
397
- { name: 'RSA', pattern: /\\brsa\\b/i },
398
- { name: 'Base64', pattern: /base64|btoa|atob/i }
399
- ];
392
+ // 已 Hook 的对象集合
393
+ const hookedObjects = new WeakSet();
394
+
395
+ // === 特征检测函数 ===
396
+
397
+ // 检测 CryptoJS 特征
398
+ function isCryptoJS(obj) {
399
+ if (!obj || typeof obj !== 'object') return false;
400
+ // CryptoJS 特征:有 AES/DES/MD5 等属性,且有 enc.Utf8
401
+ return (obj.AES && obj.AES.encrypt && obj.AES.decrypt) ||
402
+ (obj.enc && obj.enc.Utf8 && obj.enc.Hex) ||
403
+ (obj.MD5 && typeof obj.MD5 === 'function') ||
404
+ (obj.SHA256 && typeof obj.SHA256 === 'function');
405
+ }
406
+
407
+ // 检测 JSEncrypt 特征
408
+ function isJSEncrypt(obj) {
409
+ if (!obj || typeof obj !== 'function') return false;
410
+ const proto = obj.prototype;
411
+ return proto && proto.encrypt && proto.decrypt && proto.setPublicKey;
412
+ }
413
+
414
+ // 检测 SM2/SM3/SM4 国密特征
415
+ function isSMCrypto(obj) {
416
+ if (!obj || typeof obj !== 'object') return false;
417
+ return (obj.doEncrypt && obj.doDecrypt) ||
418
+ (obj.sm2 && obj.sm3) ||
419
+ (typeof obj.encrypt === 'function' && obj.cipherMode !== undefined);
420
+ }
421
+
422
+ // 检测 node-forge 特征
423
+ function isForge(obj) {
424
+ if (!obj || typeof obj !== 'object') return false;
425
+ return (obj.cipher && obj.md && obj.util) ||
426
+ (obj.pki && obj.pki.rsa);
427
+ }
428
+
429
+ // === Hook 函数 ===
430
+
431
+ // Hook CryptoJS 对象
432
+ function hookCryptoJSObject(obj, source) {
433
+ if (hookedObjects.has(obj)) return;
434
+ hookedObjects.add(obj);
435
+
436
+ // Hook 对称加密
437
+ ['AES', 'DES', 'TripleDES', 'RC4', 'Rabbit'].forEach(function(cipher) {
438
+ if (!obj[cipher]) return;
439
+ ['encrypt', 'decrypt'].forEach(function(method) {
440
+ if (!obj[cipher][method]) return;
441
+ const original = obj[cipher][method];
442
+ obj[cipher][method] = deepspider.native(function(data, key, options) {
443
+ const entry = deepspider.log('crypto', {
444
+ algo: cipher + '.' + method,
445
+ source: source,
446
+ data: String(data).slice(0, 100),
447
+ keyLen: key ? String(key).length : 0
448
+ });
449
+ deepspider.linkCrypto(entry);
450
+ return original.apply(this, arguments);
451
+ }, original);
452
+ });
453
+ });
454
+
455
+ // Hook 哈希算法
456
+ ['MD5', 'SHA1', 'SHA256', 'SHA512', 'SHA3', 'RIPEMD160'].forEach(function(algo) {
457
+ if (!obj[algo] || typeof obj[algo] !== 'function') return;
458
+ const original = obj[algo];
459
+ obj[algo] = deepspider.native(function() {
460
+ const entry = deepspider.log('crypto', {
461
+ algo: algo,
462
+ source: source,
463
+ inputLen: arguments[0] ? String(arguments[0]).length : 0
464
+ });
465
+ deepspider.linkCrypto(entry);
466
+ return original.apply(this, arguments);
467
+ }, original);
468
+ });
469
+
470
+ // Hook HMAC
471
+ ['HmacMD5', 'HmacSHA1', 'HmacSHA256', 'HmacSHA512'].forEach(function(algo) {
472
+ if (!obj[algo] || typeof obj[algo] !== 'function') return;
473
+ const original = obj[algo];
474
+ obj[algo] = deepspider.native(function() {
475
+ const entry = deepspider.log('crypto', {
476
+ algo: algo,
477
+ source: source,
478
+ inputLen: arguments[0] ? String(arguments[0]).length : 0
479
+ });
480
+ deepspider.linkCrypto(entry);
481
+ return original.apply(this, arguments);
482
+ }, original);
483
+ });
484
+
485
+ console.log('[DeepSpider] CryptoJS Hook 已启用 (来源: ' + source + ')');
486
+ }
487
+
488
+ // Hook JSEncrypt 构造函数
489
+ function hookJSEncryptObject(JSEncrypt, source) {
490
+ if (hookedObjects.has(JSEncrypt)) return;
491
+ hookedObjects.add(JSEncrypt);
492
+
493
+ const proto = JSEncrypt.prototype;
494
+ if (proto.encrypt) {
495
+ const origEnc = proto.encrypt;
496
+ proto.encrypt = deepspider.native(function(data) {
497
+ const entry = deepspider.log('crypto', {
498
+ algo: 'RSA.encrypt',
499
+ source: source,
500
+ data: String(data).slice(0, 100)
501
+ });
502
+ deepspider.linkCrypto(entry);
503
+ return origEnc.apply(this, arguments);
504
+ }, origEnc);
505
+ }
506
+ if (proto.decrypt) {
507
+ const origDec = proto.decrypt;
508
+ proto.decrypt = deepspider.native(function(data) {
509
+ const entry = deepspider.log('crypto', {
510
+ algo: 'RSA.decrypt',
511
+ source: source
512
+ });
513
+ deepspider.linkCrypto(entry);
514
+ return origDec.apply(this, arguments);
515
+ }, origDec);
516
+ }
517
+ console.log('[DeepSpider] JSEncrypt Hook 已启用 (来源: ' + source + ')');
518
+ }
400
519
 
401
- // Hook webpackJsonp
402
- function hookWebpack() {
403
- if (window.webpackJsonp && !window.webpackJsonp.__hooked__) {
404
- const orig = window.webpackJsonp.push;
405
- window.webpackJsonp.push = function(chunk) {
406
- deepspider.log('env', { action: 'webpack.chunk', id: chunk[0] });
407
- return orig.apply(this, arguments);
520
+ // Hook SM 国密对象
521
+ function hookSMCryptoObject(obj, source) {
522
+ if (hookedObjects.has(obj)) return;
523
+ hookedObjects.add(obj);
524
+
525
+ if (obj.doEncrypt) {
526
+ const origEnc = obj.doEncrypt;
527
+ obj.doEncrypt = deepspider.native(function(msg, pubKey) {
528
+ const entry = deepspider.log('crypto', {
529
+ algo: 'SM2.encrypt',
530
+ source: source,
531
+ msg: String(msg).slice(0, 100)
532
+ });
533
+ deepspider.linkCrypto(entry);
534
+ return origEnc.apply(this, arguments);
535
+ }, origEnc);
536
+ }
537
+ console.log('[DeepSpider] SM Crypto Hook 已启用 (来源: ' + source + ')');
538
+ }
539
+
540
+ // === 扫描并 Hook 模块导出 ===
541
+ function scanAndHook(exports, source) {
542
+ if (!exports || typeof exports !== 'object') return;
543
+
544
+ try {
545
+ // 直接检测导出对象
546
+ if (isCryptoJS(exports)) {
547
+ hookCryptoJSObject(exports, source);
548
+ return;
549
+ }
550
+ if (isJSEncrypt(exports)) {
551
+ hookJSEncryptObject(exports, source);
552
+ return;
553
+ }
554
+ if (isSMCrypto(exports)) {
555
+ hookSMCryptoObject(exports, source);
556
+ return;
557
+ }
558
+
559
+ // 检测 default 导出
560
+ if (exports.default) {
561
+ if (isCryptoJS(exports.default)) {
562
+ hookCryptoJSObject(exports.default, source + '.default');
563
+ } else if (isJSEncrypt(exports.default)) {
564
+ hookJSEncryptObject(exports.default, source + '.default');
565
+ }
566
+ }
567
+
568
+ // 遍历导出的属性(限制深度避免性能问题)
569
+ const keys = Object.keys(exports);
570
+ for (let i = 0; i < Math.min(keys.length, 20); i++) {
571
+ const key = keys[i];
572
+ const val = exports[key];
573
+ if (val && typeof val === 'object' && !hookedObjects.has(val)) {
574
+ if (isCryptoJS(val)) {
575
+ hookCryptoJSObject(val, source + '.' + key);
576
+ }
577
+ }
578
+ if (val && typeof val === 'function') {
579
+ if (isJSEncrypt(val)) {
580
+ hookJSEncryptObject(val, source + '.' + key);
581
+ }
582
+ }
583
+ }
584
+ } catch (e) {
585
+ // 忽略访问错误
586
+ }
587
+ }
588
+
589
+ // === Hook Webpack 模块系统 ===
590
+
591
+ // Hook Webpack 4 webpackJsonp
592
+ function hookWebpackJsonp() {
593
+ const jsonp = window.webpackJsonp;
594
+ if (!jsonp || jsonp.__deepspider_hooked__) return;
595
+
596
+ const origPush = jsonp.push;
597
+ jsonp.push = function(chunk) {
598
+ const result = origPush.apply(this, arguments);
599
+
600
+ // chunk[1] 是模块对象 { moduleId: function(module, exports, require) {} }
601
+ if (chunk && chunk[1]) {
602
+ const modules = chunk[1];
603
+ Object.keys(modules).forEach(function(moduleId) {
604
+ // 延迟扫描,等模块执行完
605
+ setTimeout(function() {
606
+ try {
607
+ // 尝试获取模块导出
608
+ if (window.__webpack_require__ && window.__webpack_require__.c) {
609
+ const mod = window.__webpack_require__.c[moduleId];
610
+ if (mod && mod.exports) {
611
+ scanAndHook(mod.exports, 'webpack:' + moduleId);
612
+ }
613
+ }
614
+ } catch (e) {}
615
+ }, 10);
616
+ });
617
+ }
618
+ return result;
619
+ };
620
+ jsonp.__deepspider_hooked__ = true;
621
+ console.log('[DeepSpider] Webpack4 jsonp Hook 已启用');
622
+ }
623
+
624
+ // Hook Webpack 5 webpackChunk
625
+ function hookWebpackChunk() {
626
+ // Webpack 5 使用 self["webpackChunk" + name]
627
+ const chunkNames = Object.keys(self).filter(function(k) {
628
+ return k.startsWith('webpackChunk');
629
+ });
630
+
631
+ chunkNames.forEach(function(name) {
632
+ const chunk = self[name];
633
+ if (!chunk || chunk.__deepspider_hooked__) return;
634
+
635
+ const origPush = chunk.push.bind(chunk);
636
+ chunk.push = function(data) {
637
+ const result = origPush(data);
638
+
639
+ // data[1] 是模块对象
640
+ if (data && data[1]) {
641
+ Object.keys(data[1]).forEach(function(moduleId) {
642
+ setTimeout(function() {
643
+ try {
644
+ // Webpack 5 的 require cache
645
+ const cache = window.__webpack_require__ && window.__webpack_require__.c;
646
+ if (cache && cache[moduleId] && cache[moduleId].exports) {
647
+ scanAndHook(cache[moduleId].exports, 'webpack5:' + moduleId);
648
+ }
649
+ } catch (e) {}
650
+ }, 10);
651
+ });
652
+ }
653
+ return result;
408
654
  };
409
- window.webpackJsonp.__hooked__ = true;
655
+ chunk.__deepspider_hooked__ = true;
656
+ console.log('[DeepSpider] Webpack5 chunk Hook 已启用: ' + name);
657
+ });
658
+ }
659
+
660
+ // Hook __webpack_require__ 直接拦截模块加载
661
+ function hookWebpackRequire() {
662
+ if (!window.__webpack_require__ || window.__webpack_require__.__deepspider_hooked__) return;
663
+
664
+ const origRequire = window.__webpack_require__;
665
+ window.__webpack_require__ = function(moduleId) {
666
+ const result = origRequire.apply(this, arguments);
667
+ // 扫描返回的模块
668
+ setTimeout(function() {
669
+ scanAndHook(result, 'require:' + moduleId);
670
+ }, 0);
671
+ return result;
672
+ };
673
+ // 复制原有属性
674
+ Object.keys(origRequire).forEach(function(key) {
675
+ window.__webpack_require__[key] = origRequire[key];
676
+ });
677
+ window.__webpack_require__.__deepspider_hooked__ = true;
678
+ console.log('[DeepSpider] __webpack_require__ Hook 已启用');
679
+ }
680
+
681
+ // 定期检查并 Hook
682
+ function checkAndHook() {
683
+ hookWebpackJsonp();
684
+ hookWebpackChunk();
685
+ hookWebpackRequire();
686
+
687
+ // 扫描已加载的模块缓存
688
+ if (window.__webpack_require__ && window.__webpack_require__.c) {
689
+ const cache = window.__webpack_require__.c;
690
+ Object.keys(cache).forEach(function(moduleId) {
691
+ if (cache[moduleId] && cache[moduleId].exports) {
692
+ scanAndHook(cache[moduleId].exports, 'cache:' + moduleId);
693
+ }
694
+ });
410
695
  }
411
696
  }
412
697
 
413
- // 定期检查
414
- const check = setInterval(hookWebpack, 100);
415
- setTimeout(function() { clearInterval(check); }, 5000);
698
+ // 启动检查
699
+ checkAndHook();
700
+ const interval = setInterval(checkAndHook, 200);
701
+ setTimeout(function() { clearInterval(interval); }, 10000);
416
702
 
417
- console.log('[DeepSpider] Webpack Hook 已启用');
703
+ console.log('[DeepSpider] Webpack Module Hook 已启用');
418
704
  })();
419
705
  `;
420
706
  }
@@ -1,38 +1,34 @@
1
1
  /**
2
2
  * DeepSpider - Hook 管理器
3
+ * 注意:Hook 脚本已由 browser/client.js 通过 defaultHooks.js 自动注入
4
+ * 此类仅用于日志管理和状态跟踪
3
5
  */
4
6
 
5
- import { cryptoHook } from './crypto.js';
6
- import { networkHook } from './network.js';
7
- import { nativeProtect } from './native.js';
8
-
9
7
  export class HookManager {
10
8
  constructor() {
11
9
  this.logs = [];
12
10
  this.onLog = null;
11
+ this.injected = false;
13
12
  }
14
13
 
15
14
  /**
16
- * 获取完整的 Hook 脚本
15
+ * 标记 Hook 已注入(由 client.js 调用)
17
16
  */
18
- getCombinedScript() {
19
- return [
20
- nativeProtect,
21
- cryptoHook,
22
- networkHook,
23
- ].join('\n\n');
17
+ markInjected() {
18
+ this.injected = true;
24
19
  }
25
20
 
26
21
  /**
27
- * 注入 Hook 到页面
22
+ * 检查是否已注入
28
23
  */
29
- async inject(page) {
30
- const script = this.getCombinedScript();
31
-
32
- // 在新文档加载前注入
33
- await page.addInitScript(script);
24
+ isInjected() {
25
+ return this.injected;
26
+ }
34
27
 
35
- // 监听 console 输出
28
+ /**
29
+ * 绑定页面 console 监听(用于收集 Hook 日志)
30
+ */
31
+ bindConsole(page) {
36
32
  page.on('console', (msg) => {
37
33
  const text = msg.text();
38
34
  if (text.includes('[DeepSpider:')) {