parse_webpack.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # -*- coding: utf-8 -*-
  2. """解析 webpack 稀疏数组,确定登录模块 P.b 引用哪个 RSA 公钥模块"""
  3. import re
  4. import sys
  5. import requests
  6. import urllib3
  7. urllib3.disable_warnings()
  8. s = requests.Session()
  9. s.verify = False
  10. t = s.get('https://newpc.996db.com/static/js/mainv2.6.01789526607.chunk.js', timeout=30).text
  11. def match_bracket(text, start):
  12. depth = 0
  13. for k in range(start, len(text)):
  14. c = text[k]
  15. if c in '([{':
  16. depth += 1
  17. elif c in ')]}':
  18. depth -= 1
  19. if depth == 0:
  20. return k
  21. return -1
  22. def split_top_array(arr_text):
  23. """[a,b,,c] → 顶层逗号切分(跳过空槽)"""
  24. items, depth, cur = [], 0, ''
  25. in_str = None
  26. for c in arr_text[1:-1]:
  27. if in_str:
  28. cur += c
  29. if c == in_str:
  30. in_str = None
  31. continue
  32. if c in '"\'':
  33. in_str = c
  34. cur += c
  35. continue
  36. if c in '([{':
  37. depth += 1
  38. elif c in ')]}':
  39. depth -= 1
  40. if c == ',' and depth == 0:
  41. items.append(cur)
  42. cur = ''
  43. continue
  44. cur += c
  45. items.append(cur)
  46. return items
  47. i = t.find('this.webpackJsonp996box||[]).push(')
  48. p1 = t.index('[', i) # [[33],[mods]]
  49. q1 = t.index(']', p1) # [33] 闭合(第一个子数组很短)
  50. r = t.index('[', q1) # mods 数组起点
  51. r_end = match_bracket(t, r)
  52. mods = split_top_array(t[r:r_end + 1])
  53. print(f'total slots: {len(mods)}, non-empty: {sum(1 for x in mods if x.strip())}')
  54. login_id = crypto_old_id = crypto_new_id = None
  55. for idx, item in enumerate(mods):
  56. if not item.strip():
  57. continue
  58. if 'AjCQvqNP' in item:
  59. crypto_old_id = idx
  60. print(f'crypto OLD-key module id={idx} size={len(item)} exports={re.findall(r"n\.d\(t,.(\w+).", item)}')
  61. if 'KuB+7dLm' in item:
  62. crypto_new_id = idx
  63. print(f'crypto NEW-key module id={idx} size={len(item)} exports={re.findall(r"n\.d\(t,.(\w+).", item)}')
  64. if 'function _e()' in item:
  65. login_id = idx
  66. imps = re.findall(r'(\w+)=n\((\d+)\)', item)
  67. print(f'LOGIN module id={idx} size={len(item)}')
  68. print(f' imports: {imps}')
  69. # 在登录模块里找 P 的绑定
  70. login_mod = mods[login_id] if login_id is not None else ''
  71. for m in re.finditer(r'(\w+)=n\.n?\((\d+)\)', login_mod):
  72. name, num = m.group(1), m.group(2)
  73. if name == 'P':
  74. print(f'\n*** P = n({num})')
  75. target = int(num)
  76. if target == crypto_old_id:
  77. print('*** P 指向 OLD 公钥模块 (b=old key)')
  78. elif target == crypto_new_id:
  79. print('*** P 指向 NEW 公钥模块 (a=new key)')
  80. else:
  81. print(f'*** P 指向模块 {target}(非公钥模块!)其内容片段:')
  82. if target < len(mods):
  83. print(mods[target][:600])