🔍 核心机关与修改指南
🧩 7步逆向解混淆 unscramble()
复刻了前端 JS 打包代码里的数组切片移位算法,算出真实的 SM4 密钥与初始化向量。
💻 完整源码与配置内容(共 47 行 · 支持行号定位与一键复制)
| 1 | #!/usr/bin/env python3 |
| 2 | """PrimeHub API client - SM4-CBC encrypted transport, reverse-engineered from web bundle. |
| 3 | Usage: primehub_client.py <path> '<json-params>' |
| 4 | """ |
| 5 | import json, urllib.request, urllib.parse, uuid, time, sys |
| 6 | from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT |
| 7 | |
| 8 | IV_S = '38fed878322f002e58db85fdf0417a0f' |
| 9 | KEY_S = '160a505137ed9d6efbf089ebfec49a93' |
| 10 | def unscramble(s): |
| 11 | e = list(s); n = [[1,3],[3,8],[2,5],[1,9],[11,15],[11,15],[1,3]] |
| 12 | for t in reversed(n): |
| 13 | L = len(e)-(t[1]-t[0]); seg = e[L:]; del e[L:]; e[t[0]:t[0]] = seg |
| 14 | return ''.join(e) |
| 15 | KEY = bytes.fromhex(unscramble(KEY_S)); IV = bytes.fromhex(unscramble(IV_S)) |
| 16 | BASE = 'https://primehub.primebot.com' |
| 17 | |
| 18 | def call(path, params, token=None): |
| 19 | params = dict(params); params['timestamp'] = int(time.time()*1000); params['requestId'] = str(uuid.uuid4()) |
| 20 | t = json.dumps(params, separators=(',',':')) |
| 21 | inner = urllib.parse.quote(t, safe='') |
| 22 | c = CryptSM4(); c.set_key(KEY, SM4_ENCRYPT) |
| 23 | ct = c.crypt_cbc(IV, inner.encode()).hex() |
| 24 | body = json.dumps({'data': ct}).encode() |
| 25 | h = {'Content-Type':'application/json','request-t':'true','X-Request-Id':'req-'+uuid.uuid4().hex[:8], |
| 26 | 'Origin':BASE,'Referer':BASE+'/'} |
| 27 | if token: h['Authorization'] = 'Bearer '+token |
| 28 | req = urllib.request.Request(BASE+path, data=body, headers=h) |
| 29 | try: |
| 30 | r = urllib.request.urlopen(req, timeout=30) |
| 31 | raw = r.read().decode() |
| 32 | except urllib.error.HTTPError as ex: |
| 33 | raw = ex.read().decode() |
| 34 | d = json.loads(raw) |
| 35 | if isinstance(d.get('data'), str) and d.get('code') == 1000: |
| 36 | try: |
| 37 | c2 = CryptSM4(); c2.set_key(KEY, SM4_DECRYPT) |
| 38 | pt = c2.crypt_cbc(IV, bytes.fromhex(d['data'])) |
| 39 | d['data'] = json.loads(urllib.parse.unquote(pt.decode())) |
| 40 | except Exception as e: |
| 41 | d['_decrypt_error'] = str(e) |
| 42 | return d |
| 43 | |
| 44 | if __name__ == '__main__': |
| 45 | path = sys.argv[1]; params = json.loads(sys.argv[2]) if len(sys.argv)>2 else {} |
| 46 | token = sys.argv[3] if len(sys.argv)>3 else None |
| 47 | print(json.dumps(call(path, params, token), ensure_ascii=False, indent=1)[:4000]) |