import base64 import binascii import ctypes import gzip import hashlib import json import time from urllib.parse import quote, urlparse, parse_qs, quote_plus from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad def encrypt_aes_cbc(plaintext): key = "z7Jut6Ywr2Pe5Nhx".encode("utf-8") iv = "0807060504030201".encode("utf-8") cipher = AES.new(key, AES.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(bytes.fromhex(plaintext), AES.block_size)) return base64.b64encode(ciphertext).decode() def decrypt_aes_128_cbc(ciphertext): """ """ key = "z7Jut6Ywr2Pe5Nhx".encode("utf-8") iv = "0807060504030201".encode("utf-8") cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(base64.b64decode(ciphertext)) strs = binascii.b2a_hex(plaintext).decode() print(strs, len(strs)) return binascii.b2a_hex(unpad(plaintext, AES.block_size)) def md5_(bytearray): md5 = hashlib.md5() md5.update(bytearray) result = md5.hexdigest() # print(result) return result def hex_2_str(v: int): v = v & 0xffffffff # 将结果转换为32位有符号整数 return str(hex(v))[2:] def get_md5Array(hex_md5): md5_list = [hex_md5[i: i + 8] for i in range(0, len(hex_md5), 8)] for j in range(len(md5_list)): m = [md5_list[j][i: i + 2] for i in range(0, len(md5_list[j]), 2)] md5_list[j] = "".join(m[::-1]) # print(md5_list) md5_int_list = [] for q in md5_list: decimal_num = int(q, 16) # decimal_num = is_overflow(decimal_num) md5_int_list.append(decimal_num) # print(md5_int_list) return md5_int_list def hex_reserve(i): st1 = hex_2_str(i) st1 = '0' * (8 - len(st1)) + st1 if len(st1) < 8 else st1 return "".join([st1[i: i + 2] for i in range(0, len(st1), 2)][::-1]) def logical_left_shift(n, bits): # 有符号左移 return ctypes.c_int(n << bits).value def logical_right_shift(n, bits): # 无符号右移 return (n % 0x100000000) >> bits def python_2_js(value, size): # python转为和js一样 ^ return (value ^ size) % (2**32) def iu(jo): jp = [] for jq in range(0, len(jo), 2): jr = jo[jq:jq + 2] js = int(jr, 16) jp.append(js) # print(jp) return jp def ix(jo): if jo is None: jo = [] jp = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] result = "".join([jp[jo_item >> 4 & 15] + jp[15 & jo_item] for jo_item in jo]) return result def is_(jo): jp = quote(jo) # print(jp) jq = [] jr = 0 while jr < len(jp): js = jp[jr] if "%" == js: jt = jp[jr + 1] + jp[jr + 2] ju = int(jt, 16) jq.append(ju) jr += 2 else: jq.append(ord(js[0])) jr += 1 # print(len(jq), jq) return jq def _iD(jo): raw_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' new_table = 'ZmserbBoHQtNP+wOcza/LpngG8yJq42KWYj0DSfdikx3VT16IlUAFM97hECvuRX5=' dictionary_decode = str.maketrans(new_table, raw_table) # 创建字符映射关系 用于base64decode dictionary_encode = dict(zip(dictionary_decode.values(), dictionary_decode.keys())) # 创建一个与上面反向的映射关系用于base64encode result_b64 = base64.b64encode(bytearray(jo)).decode() new_result_b64 = result_b64.translate(dictionary_encode) # print(new_result_b64) return new_result_b64 def get_k2(jo, jp): """ iO(jo, jp) jo: url相关的数组 jp: 时间戳 """ m = len(jo) % 4 jj = jp ^ len(jo) ju = 1540483477 # 固定 # 每四个为一组 for i in range(0, len(jo) - m, 4): tmp1 = (jo[i] & 255) | logical_left_shift((jo[i + 1] & 255), 8) tmp2 = logical_left_shift((jo[i + 2] & 255), 16) tmp3 = tmp1 | tmp2 tmp4 = tmp3 | logical_left_shift(jo[i + 3] & 255, 24) tmp5 = (tmp4 & 65535) * ju tmp6 = logical_left_shift((((logical_right_shift(tmp4, 16)) * ju) & 65535), 16) tmp7 = tmp5 + tmp6 tmp8 = python_2_js(tmp7, logical_right_shift(tmp7, 24)) tmp9 = (tmp8 & 65535) * ju tmp10 = logical_left_shift((((logical_right_shift(tmp8, 16)) * ju) & 65535), 16) tmp11 = tmp9 + tmp10 tmp12 = (jj & 65535) * ju tmp13 = logical_left_shift((((logical_right_shift(jj, 16)) * ju) & 65535), 16) tmp14 = tmp12 + tmp13 jj = python_2_js(tmp11, tmp14) i = i + 4 if m == 3: jj = python_2_js(jj , logical_left_shift(255 & jo[i + 2], 16)) jj = python_2_js(jj , logical_left_shift(255 & jo[i + 1], 8)) jj = python_2_js(jj, (255 & jo[i])) jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16) elif m == 2: jj = python_2_js(jj , logical_left_shift(255 & jo[i + 1], 8)) jj = python_2_js(jj, (255 & jo[i])) jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16) elif m == 1: jj = python_2_js(jj, (255 & jo[i])) jj = (65535 & jj) * ju + logical_left_shift(logical_right_shift(jj, 16) * ju & 65535, 16) tmp0 = python_2_js(jj, logical_right_shift(jj, 13)) tmp1 = (tmp0 & 65535) * ju tmp2 = logical_left_shift((logical_right_shift(tmp0, 16) * ju) & 65535, 16) tmp3 = tmp1 + tmp2 tmp4 = logical_right_shift(python_2_js(tmp3, logical_right_shift(tmp3, 15)), 0) res = ctypes.c_int(tmp4 ^ ju).value # print(res) return res def iy(jo, jq): jr = 0 js = 0 jt = [] ju = len(jq) # print(f"{jq} 长度为 {ju}") for jv in range(ju): jr = (jr + 1) % 256 js = (js + jo[jr]) % 256 jp = jo[jr] jo[jr] = jo[js] jo[js] = jp jt.append(ord(jq[jv]) ^ jo[(jo[jr] + jo[js]) % 256]) return jt def get_k1(jZ, jp): array1 = [i for i in range(256)] array2 = jZ jr = 0 for i in range(256): jt = i % 256 n = i % 16 js = array1[jt] jr = (jr + js + array2[n] + 31) % 256 array1[jt] = array1[jr] array1[jr] = js # print(f"ararry1 => {array1}") jt_ = iy(array1, jp) # print(f"_arrary1 => {array1}") jZ.extend(jt_) # print(f" jZ => {len(jZ)} => {jZ}") k1 = _iD(jZ) # a5 return k1 def get_kh(jS, kb, kf, x0=4): m = [0] * 2 m[0] = jS << x0 m[1] = jS << (32 - x0) m[0] = m[0] | m[1] kg = m[0] kf[0] = kf[0] ^ kg kf[1] = kf[1] ^ kb kf[2] = kf[2] ^ kb ^ kg kf[3] = kf[3] ^ kf[0] kh_list = [] for i in kf: kh_list.append(hex_reserve(i)) kh = "".join(kh_list) return kh def iv(jo): jp = [] jp.append((jo >> 24) & 255) jp.append((jo >> 16) & 255) jp.append((jo >> 8) & 255) jp.append(jo & 255) return jp def bc(p): t = 256 f = [None] * t while t: c = 8 t -= 1 a = t while c: if a & 1: a = python_2_js(logical_right_shift(a, 1), 3988292384) else: a = logical_right_shift(a, 1) c -= 1 f[t] = logical_right_shift(a, 0) c = 0 t = -1 while c < len(p): t = f[python_2_js(255 & t, p[c])] ^ logical_right_shift(t, 8) c += 1 return python_2_js(t, 306674911) - 2 ** 32 def get_a6(a6): """ 直接读取a6 并修改a6的部分内容 :return: """ env = from_a6(a6[4:]) A = gzip.compress(env.encode("utf-8"), compresslevel=1) C = encrypt_aes_cbc(A.hex()) C = "w1.3" + C return C def get_JP(url): """ 处理url """ parse_result = urlparse(url) path = parse_result.path param_dict = parse_qs(parse_result.query) param_list = list(param_dict.keys()) param_list.sort() # 排序从小到大 params = "&".join([f'{i}={quote_plus(param_dict[i][0])}' for i in param_list]) target_ = f"GET {path} {params}" JP = is_(target_) return JP def get_a5(i, y, qa): """ a5 生成 """ d = i & 4294967295 p = iv(d) g = [] g.extend(is_(y)) g.extend(p) v = md5_(bytearray(g)) m = iu(v[:15]) f = 0 # env 环境值 m[7] = 255 & (f ^ bc(p)) m.extend(p) l = iv(4294967295 & bc(m)) m.extend(l) a5 = get_k1(m, qa) return a5 def get_main(i, a6, a3, a7, url, jq): # i = 1723618362188 # 时间戳 TODO:需要二次处理 o = i & 4294967295 a5 = get_a5(i, a6, jq) tmp = is_(a6) l = iv(o) tmp.extend(l) g = bytearray(tmp) v = md5_(g) w = get_JP(url) S = get_k2(w, i) C = iv(S) A = get_k2(bytearray(is_(a5)), i) I = iv(A) c = [S, A, S ^ o, S ^ A ^ o] O = iu("".join([hex_reserve(i) for i in c])) tmp = [] tmp.extend(C) tmp.extend(I) tmp.extend(O) D = ix(tmp) # a4 d = logical_right_shift(A, 0) T = f"1.2{i}{a3}{D}{d}{v}{a7}" tmp = md5_(bytearray(is_(T))) j = get_md5Array(tmp) d1 = get_kh(o, d, j, x0=3) # 到这一步就是 小程序的 d1 mtgsig = { "a1": "1.2", "a2": i, # 时间戳 估计有校验 "a3": a3, # 服务器下发 "a4": D, # 计算 "a5": a5, # 计算 暂时不清楚入参 "a6": a6, # 计算环境 需要确定环境信息 "a7": a7, # wxcode 固定 "x0": 3, # 固定 "d1": d1 # 计算 比h5少一步 } return mtgsig def from_a6(a6): """ 根据版本迭代 a6 :param a6: :return: """ decode_res = decrypt_aes_128_cbc(a6) plain_text = gzip.decompress(bytes.fromhex(decode_res.decode())) p = json.loads(plain_text.decode()) p[-3] = int(time.time()) # 修改时间戳 p[-4][-6] = "3.9.12" # 修改版本 p[-4][-9] = "3.6.3" # 修改另外一个版本 **目前没有看到其他的需要修改的地方 res = json.dumps(p, ensure_ascii=False, separators=(',', ':')) return res def mtgsig_run(url, b8, a3, a6): """ mtgsig 对外调用接口 :param url: 需要加密的链接 :param b8: 累加 :param a3: a3服务器下发 暂时写死 :param a6: 目标设备环境 :return: """ a6 = get_a6(a6) a7 = "wx734c1ad7b3562129" # m = 1723688535465 i = int(time.time() * 1000) # diff = i - m # i = i - diff # 处理时间 这块暂时先写死 _jq = {"b7": int(time.time()) - 200, "b1": {"miniProgram": {"appId": "wx734c1ad7b3562129", "envVersion": "release", "version": "9.37.232"}}, "b6": "", "b8": b8, "b2": "pages/poi/poi"} jq = json.dumps(_jq, separators=(',', ':')) mtgsig = json.dumps(get_main(i, a6, a3, a7, url, jq), ensure_ascii=False, separators=(',', ':')) return mtgsig if __name__ == '__main__': 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" # print(mtgsig_run(url)) a6 = "w1.3grvNcRFZkE6QoEQ31ALPuc415J6/VKamSdeIXCVWX/DGsGA+taQlBpn7QF0z/ffrSmLkkQW0dqtDnWECJyGr9iFEhTKHYlyKRbYWwffNlvlDZ9tmFet/PyCSfKJw7xVZe8/4QYaAcbk1eJ7tWMUQllVcQDKlxku5Smml4r566JVAOGi48LCGAHxImhjIuI2KomDqOtCQb7Ii2cbGd2Y7vmWjypGGJbTtrztH/1weiEULAxadX72+RdjpXDa8UMgGbVmEUu5LLIwoyNEi6leK6f/QvV9/EvVfb5vtKyL5IAQND5B9g5qndP4GOuTftOM2ylQKb4rguR4TEQsR3uyruBJusZlID67k1d8jPCM+ol2mbFJP88EfBwnrQ+BSDWfVQjjfIj2mWpEnI+oA72gkPxuHteESJaXzZz94udFusf1tUtdf0q15QiQPSBX7JjK2P/lLHMfe+867ZiGMyoivxisrw1d9bbal27BjMTm32IrAN+ywQFoP9bHXNThdxmai" print(get_a6(a6)) print(from_a6(a6[4:]))