算法暴露接口(xhs、dy、ks、wx、hnw)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
12 KiB

7 months ago
  1. import base64
  2. import binascii
  3. import ctypes
  4. import gzip
  5. import hashlib
  6. import json
  7. import time
  8. from urllib.parse import quote, urlparse, parse_qs, quote_plus
  9. from Crypto.Cipher import AES
  10. from Crypto.Util.Padding import pad, unpad
  11. def encrypt_aes_cbc(plaintext):
  12. key = "z7Jut6Ywr2Pe5Nhx".encode("utf-8")
  13. iv = "0807060504030201".encode("utf-8")
  14. cipher = AES.new(key, AES.MODE_CBC, iv)
  15. ciphertext = cipher.encrypt(pad(bytes.fromhex(plaintext), AES.block_size))
  16. return base64.b64encode(ciphertext).decode()
  17. def decrypt_aes_128_cbc(ciphertext):
  18. """
  19. """
  20. key = "z7Jut6Ywr2Pe5Nhx".encode("utf-8")
  21. iv = "0807060504030201".encode("utf-8")
  22. cipher = AES.new(key, AES.MODE_CBC, iv)
  23. plaintext = cipher.decrypt(base64.b64decode(ciphertext))
  24. strs = binascii.b2a_hex(plaintext).decode()
  25. print(strs, len(strs))
  26. return binascii.b2a_hex(unpad(plaintext, AES.block_size))
  27. def md5_(bytearray):
  28. md5 = hashlib.md5()
  29. md5.update(bytearray)
  30. result = md5.hexdigest()
  31. # print(result)
  32. return result
  33. def hex_2_str(v: int):
  34. v = v & 0xffffffff # 将结果转换为32位有符号整数
  35. return str(hex(v))[2:]
  36. def get_md5Array(hex_md5):
  37. md5_list = [hex_md5[i: i + 8] for i in range(0, len(hex_md5), 8)]
  38. for j in range(len(md5_list)):
  39. m = [md5_list[j][i: i + 2] for i in range(0, len(md5_list[j]), 2)]
  40. md5_list[j] = "".join(m[::-1])
  41. # print(md5_list)
  42. md5_int_list = []
  43. for q in md5_list:
  44. decimal_num = int(q, 16)
  45. # decimal_num = is_overflow(decimal_num)
  46. md5_int_list.append(decimal_num)
  47. # print(md5_int_list)
  48. return md5_int_list
  49. def hex_reserve(i):
  50. st1 = hex_2_str(i)
  51. st1 = '0' * (8 - len(st1)) + st1 if len(st1) < 8 else st1
  52. return "".join([st1[i: i + 2] for i in range(0, len(st1), 2)][::-1])
  53. def logical_left_shift(n, bits):
  54. # 有符号左移
  55. return ctypes.c_int(n << bits).value
  56. def logical_right_shift(n, bits):
  57. # 无符号右移
  58. return (n % 0x100000000) >> bits
  59. def python_2_js(value, size):
  60. # python转为和js一样 ^
  61. return (value ^ size) % (2**32)
  62. def iu(jo):
  63. jp = []
  64. for jq in range(0, len(jo), 2):
  65. jr = jo[jq:jq + 2]
  66. js = int(jr, 16)
  67. jp.append(js)
  68. # print(jp)
  69. return jp
  70. def ix(jo):
  71. if jo is None:
  72. jo = []
  73. jp = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
  74. result = "".join([jp[jo_item >> 4 & 15] + jp[15 & jo_item] for jo_item in jo])
  75. return result
  76. def is_(jo):
  77. jp = quote(jo)
  78. # print(jp)
  79. jq = []
  80. jr = 0
  81. while jr < len(jp):
  82. js = jp[jr]
  83. if "%" == js:
  84. jt = jp[jr + 1] + jp[jr + 2]
  85. ju = int(jt, 16)
  86. jq.append(ju)
  87. jr += 2
  88. else:
  89. jq.append(ord(js[0]))
  90. jr += 1
  91. # print(len(jq), jq)
  92. return jq
  93. def _iD(jo):
  94. raw_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
  95. new_table = 'ZmserbBoHQtNP+wOcza/LpngG8yJq42KWYj0DSfdikx3VT16IlUAFM97hECvuRX5='
  96. dictionary_decode = str.maketrans(new_table, raw_table) # 创建字符映射关系 用于base64decode
  97. dictionary_encode = dict(zip(dictionary_decode.values(), dictionary_decode.keys())) # 创建一个与上面反向的映射关系用于base64encode
  98. result_b64 = base64.b64encode(bytearray(jo)).decode()
  99. new_result_b64 = result_b64.translate(dictionary_encode)
  100. # print(new_result_b64)
  101. return new_result_b64
  102. def get_k2(jo, jp):
  103. """
  104. iO(jo, jp)
  105. jo: url相关的数组
  106. jp:
  107. """
  108. m = len(jo) % 4
  109. jj = jp ^ len(jo)
  110. ju = 1540483477 # 固定
  111. # 每四个为一组
  112. for i in range(0, len(jo) - m, 4):
  113. tmp1 = (jo[i] & 255) | logical_left_shift((jo[i + 1] & 255), 8)
  114. tmp2 = logical_left_shift((jo[i + 2] & 255), 16)
  115. tmp3 = tmp1 | tmp2
  116. tmp4 = tmp3 | logical_left_shift(jo[i + 3] & 255, 24)
  117. tmp5 = (tmp4 & 65535) * ju
  118. tmp6 = logical_left_shift((((logical_right_shift(tmp4, 16)) * ju) & 65535), 16)
  119. tmp7 = tmp5 + tmp6
  120. tmp8 = python_2_js(tmp7, logical_right_shift(tmp7, 24))
  121. tmp9 = (tmp8 & 65535) * ju
  122. tmp10 = logical_left_shift((((logical_right_shift(tmp8, 16)) * ju) & 65535), 16)
  123. tmp11 = tmp9 + tmp10
  124. tmp12 = (jj & 65535) * ju
  125. tmp13 = logical_left_shift((((logical_right_shift(jj, 16)) * ju) & 65535), 16)
  126. tmp14 = tmp12 + tmp13
  127. jj = python_2_js(tmp11, tmp14)
  128. i = i + 4
  129. if m == 3:
  130. jj = python_2_js(jj , logical_left_shift(255 & jo[i + 2], 16))
  131. jj = python_2_js(jj , logical_left_shift(255 & jo[i + 1], 8))
  132. jj = python_2_js(jj, (255 & jo[i]))
  133. jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16)
  134. elif m == 2:
  135. jj = python_2_js(jj , logical_left_shift(255 & jo[i + 1], 8))
  136. jj = python_2_js(jj, (255 & jo[i]))
  137. jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16)
  138. elif m == 1:
  139. jj = python_2_js(jj, (255 & jo[i]))
  140. jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16)
  141. tmp0 = python_2_js(jj, logical_right_shift(jj, 13))
  142. tmp1 = (tmp0 & 65535) * ju
  143. tmp2 = logical_left_shift((logical_right_shift(tmp0, 16) * ju) & 65535, 16)
  144. tmp3 = tmp1 + tmp2
  145. tmp4 = logical_right_shift(python_2_js(tmp3, logical_right_shift(tmp3, 15)), 0)
  146. res = ctypes.c_int(tmp4 ^ ju).value
  147. # print(res)
  148. return res
  149. def iy(jo, jq):
  150. jr = 0
  151. js = 0
  152. jt = []
  153. ju = len(jq)
  154. # print(f"{jq} 长度为 {ju}")
  155. for jv in range(ju):
  156. jr = (jr + 1) % 256
  157. js = (js + jo[jr]) % 256
  158. jp = jo[jr]
  159. jo[jr] = jo[js]
  160. jo[js] = jp
  161. jt.append(ord(jq[jv]) ^ jo[(jo[jr] + jo[js]) % 256])
  162. return jt
  163. def get_k1(jZ, jp):
  164. array1 = [i for i in range(256)]
  165. array2 = jZ
  166. jr = 0
  167. for i in range(256):
  168. jt = i % 256
  169. n = i % 16
  170. js = array1[jt]
  171. jr = (jr + js + array2[n] + 31) % 256
  172. array1[jt] = array1[jr]
  173. array1[jr] = js
  174. # print(f"ararry1 => {array1}")
  175. jt_ = iy(array1, jp)
  176. # print(f"_arrary1 => {array1}")
  177. jZ.extend(jt_)
  178. # print(f" jZ => {len(jZ)} => {jZ}")
  179. k1 = _iD(jZ) # a5
  180. return k1
  181. def get_kh(jS, kb, kf, x0=4):
  182. m = [0] * 2
  183. m[0] = jS << x0
  184. m[1] = jS << (32 - x0)
  185. m[0] = m[0] | m[1]
  186. kg = m[0]
  187. kf[0] = kf[0] ^ kg
  188. kf[1] = kf[1] ^ kb
  189. kf[2] = kf[2] ^ kb ^ kg
  190. kf[3] = kf[3] ^ kf[0]
  191. kh_list = []
  192. for i in kf:
  193. kh_list.append(hex_reserve(i))
  194. kh = "".join(kh_list)
  195. return kh
  196. def iv(jo):
  197. jp = []
  198. jp.append((jo >> 24) & 255)
  199. jp.append((jo >> 16) & 255)
  200. jp.append((jo >> 8) & 255)
  201. jp.append(jo & 255)
  202. return jp
  203. def bc(p):
  204. t = 256
  205. f = [None] * t
  206. while t:
  207. c = 8
  208. t -= 1
  209. a = t
  210. while c:
  211. if a & 1:
  212. a = python_2_js(logical_right_shift(a, 1), 3988292384)
  213. else:
  214. a = logical_right_shift(a, 1)
  215. c -= 1
  216. f[t] = logical_right_shift(a, 0)
  217. c = 0
  218. t = -1
  219. while c < len(p):
  220. t = f[python_2_js(255 & t, p[c])] ^ logical_right_shift(t, 8)
  221. c += 1
  222. return python_2_js(t, 306674911) - 2 ** 32
  223. def get_a6(a6):
  224. """
  225. a6
  226. a6的部分内容
  227. :return:
  228. """
  229. env = from_a6(a6[4:])
  230. A = gzip.compress(env.encode("utf-8"), compresslevel=1)
  231. C = encrypt_aes_cbc(A.hex())
  232. C = "w1.3" + C
  233. return C
  234. def get_JP(url):
  235. """
  236. url
  237. """
  238. parse_result = urlparse(url)
  239. path = parse_result.path
  240. param_dict = parse_qs(parse_result.query)
  241. param_list = list(param_dict.keys())
  242. param_list.sort() # 排序从小到大
  243. params = "&".join([f'{i}={quote_plus(param_dict[i][0])}' for i in param_list])
  244. target_ = f"GET {path} {params}"
  245. JP = is_(target_)
  246. return JP
  247. def get_a5(i, y, qa):
  248. """
  249. a5
  250. """
  251. d = i & 4294967295
  252. p = iv(d)
  253. g = []
  254. g.extend(is_(y))
  255. g.extend(p)
  256. v = md5_(bytearray(g))
  257. m = iu(v[:15])
  258. f = 0 # env 环境值
  259. m[7] = 255 & (f ^ bc(p))
  260. m.extend(p)
  261. l = iv(4294967295 & bc(m))
  262. m.extend(l)
  263. a5 = get_k1(m, qa)
  264. return a5
  265. def get_main(i, a6, a3, a7, url, jq):
  266. # i = 1723618362188 # 时间戳 TODO:需要二次处理
  267. o = i & 4294967295
  268. a5 = get_a5(i, a6, jq)
  269. tmp = is_(a6)
  270. l = iv(o)
  271. tmp.extend(l)
  272. g = bytearray(tmp)
  273. v = md5_(g)
  274. w = get_JP(url)
  275. S = get_k2(w, i)
  276. C = iv(S)
  277. A = get_k2(bytearray(is_(a5)), i)
  278. I = iv(A)
  279. c = [S, A, S ^ o, S ^ A ^ o]
  280. O = iu("".join([hex_reserve(i) for i in c]))
  281. tmp = []
  282. tmp.extend(C)
  283. tmp.extend(I)
  284. tmp.extend(O)
  285. D = ix(tmp) # a4
  286. d = logical_right_shift(A, 0)
  287. T = f"1.2{i}{a3}{D}{d}{v}{a7}"
  288. tmp = md5_(bytearray(is_(T)))
  289. j = get_md5Array(tmp)
  290. d1 = get_kh(o, d, j, x0=3) # 到这一步就是 小程序的 d1
  291. mtgsig = {
  292. "a1": "1.2",
  293. "a2": i, # 时间戳 估计有校验
  294. "a3": a3, # 服务器下发
  295. "a4": D, # 计算
  296. "a5": a5, # 计算 暂时不清楚入参
  297. "a6": a6, # 计算环境 需要确定环境信息
  298. "a7": a7, # wxcode 固定
  299. "x0": 3, # 固定
  300. "d1": d1 # 计算 比h5少一步
  301. }
  302. return mtgsig
  303. def from_a6(a6):
  304. """
  305. a6
  306. :param a6:
  307. :return:
  308. """
  309. decode_res = decrypt_aes_128_cbc(a6)
  310. plain_text = gzip.decompress(bytes.fromhex(decode_res.decode()))
  311. p = json.loads(plain_text.decode())
  312. p[-3] = int(time.time()) # 修改时间戳
  313. p[-4][-6] = "3.9.12" # 修改版本
  314. p[-4][-9] = "3.6.3" # 修改另外一个版本 **目前没有看到其他的需要修改的地方
  315. res = json.dumps(p, ensure_ascii=False, separators=(',', ':'))
  316. return res
  317. def mtgsig_run(url, b8, a3, a6):
  318. """
  319. mtgsig
  320. :param url:
  321. :param b8:
  322. :param a3: a3服务器下发
  323. :param a6:
  324. :return:
  325. """
  326. a6 = get_a6(a6)
  327. a7 = "wx734c1ad7b3562129"
  328. # m = 1723688535465
  329. i = int(time.time() * 1000)
  330. # diff = i - m
  331. # i = i - diff # 处理时间 这块暂时先写死
  332. _jq = {"b7": int(time.time()) - 200,
  333. "b1": {"miniProgram": {"appId": "wx734c1ad7b3562129", "envVersion": "release", "version": "9.37.232"}},
  334. "b6": "", "b8": b8, "b2": "pages/poi/poi"}
  335. jq = json.dumps(_jq, separators=(',', ':'))
  336. mtgsig = json.dumps(get_main(i, a6, a3, a7, url, jq), ensure_ascii=False, separators=(',', ':'))
  337. return mtgsig
  338. if __name__ == '__main__':
  339. url = "https://mapi.dianping.com/mapi/wechat/weshop.bin?yodaReady=wx&csecappid=wx734c1ad7b3562129&csecplatform=3&csecversionname=9.64.0&csecversion=1.4.0&optimus_uuid=18b229261a3c8-c53ac1e606702-0-0-18b229261a3c8&optimus_platform=13&optimus_partner=203&optimus_risk_level=71&optimus_code=10&redirect_from=pages%252Fdetail%252Fdetail&shopUuid=k9Kcg8I6w4GBcuGF&shopType=10&shopStyle=bigpic&online=1&shopuuid=k9Kcg8I6w4GBcuGF&pageName=shop&lat=40.055335628194634&lng=116.35128357647478&mtsiReferrer=pages%252Fdetail%252Fdetail%253Fredirect_from%253Dpages%25252Fdetail%25252Fdetail%2526shopUuid%253Dk9Kcg8I6w4GBcuGF%2526shopType%253D10%2526shopStyle%253Dbigpic%2526online%253D1%2526shopuuid%253Dk9Kcg8I6w4GBcuGF%2526shopId%253Dk9Kcg8I6w4GBcuGF%2526pageName%253Dshop&cookieid=-4TzrChF1yPgyQBn5D7NrKC2_vuFu9oL8DiAq28T-Kk&device_system=MAC&wxmp_version=9.64.0"
  340. # print(mtgsig_run(url))
  341. a6 = "w1.3grvNcRFZkE6QoEQ31ALPuc415J6/VKamSdeIXCVWX/DGsGA+taQlBpn7QF0z/ffrSmLkkQW0dqtDnWECJyGr9iFEhTKHYlyKRbYWwffNlvlDZ9tmFet/PyCSfKJw7xVZe8/4QYaAcbk1eJ7tWMUQllVcQDKlxku5Smml4r566JVAOGi48LCGAHxImhjIuI2KomDqOtCQb7Ii2cbGd2Y7vmWjypGGJbTtrztH/1weiEULAxadX72+RdjpXDa8UMgGbVmEUu5LLIwoyNEi6leK6f/QvV9/EvVfb5vtKyL5IAQND5B9g5qndP4GOuTftOM2ylQKb4rguR4TEQsR3uyruBJusZlID67k1d8jPCM+ol2mbFJP88EfBwnrQ+BSDWfVQjjfIj2mWpEnI+oA72gkPxuHteESJaXzZz94udFusf1tUtdf0q15QiQPSBX7JjK2P/lLHMfe+867ZiGMyoivxisrw1d9bbal27BjMTm32IrAN+ywQFoP9bHXNThdxmai"
  342. print(get_a6(a6))
  343. print(from_a6(a6[4:]))