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.
431 lines
12 KiB
431 lines
12 KiB
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:]))
|