xyb小程序加密逻辑逆向
前言
近期学校开始让在外实习的,每天都得打卡校友邦,再加上外地朋友也由于种种原因要回学校,但还要打卡,就开始研究了这个东西,但是呢以前写过,回过头跑程序发现报错,估摸着那边改了或者加了防护,前面签到啥的接口没什么好提的,没啥难度,这次记录的是签退和重签的加密逆向过程。
过程
通过抓包分析,我们知道了重签和签退是通过这个接口实现的
https://xcx.xybsyw.com/student/clock/Post!updateClock.action
其中请求头中 Cookie
我们可以通过接口获取,v
和 n
为固定值,而 t
、s
、m
三个参数是通过代码生成的
通过使用 pc_wxapkg_decrypt
将手机本地小程序源码取出来,分析源码,解包后发现代码不太完整但是不影响,全局搜索 token
发现关键代码
简单格式化了一下,具体代码如下,关键的部分就是我写注释的地方
getTokenData: function(e, a) {
// 定义一个包含62个数字和字母的数组t和一个包含0~61的数字的数组n
for (var t = ["5", "b", "f", "A", "J", "Q", "g", "a", "l", "p", "s", "q", "H", "4", "L", "Q", "g", "1", "6", "Q", "Z", "v", "w", "b", "c", "e", "2", "2", "m", "l", "E", "g", "G", "H", "I", "r", "o", "s", "d", "5", "7", "x", "t", "J", "S", "T", "F", "v", "w", "4", "8", "9", "0", "K", "E", "3", "4", "0", "m", "r", "i", "n"], n = [], o = 0; o < 62; o++) n.push(o + "");
// 获取当前时间戳
var i = Math.round((new Date).getTime() / 1e3),
// 对数组n进行乱序操作,截取前20个字符作为iArrStr属性的值
r = function(e, a) {
for (var t, n, o = e.slice(0), i = e.length, r = i - a; i-- > r;) t = o[n = Math.floor((i + 1) * Math.random())], o[n] = o[i], o[i] = t;
return o.slice(r)
}(n, 20),
// 定义一个空字符串s,用于存储拼接后的属性值
s = "";
// 遍历数组r,将对应位置的字符从数组t中取出,拼接到字符串s中
r.forEach((function(e, a) {
s += t[e]
}));
// 定义一个空字符串d,用于存储拼接后的属性值
var g, p = function(e) {
for (var a = Object.keys(e).sort(), t = {}, n = 0; n < a.length; n++) t[a[n]] = e[a[n]];
return t
}(e),
d = "";
for (g in p) {
if (-1 != ["content", "deviceName", "keyWord", "blogBody", "blogTitle", "getType", "responsibilities", "street", "text", "reason", "searchvalue", "key", "answers", "leaveReason", "personRemark", "selfAppraisal", "imgUrl", "wxname", "deviceId", "avatarTempPath", "file", "file", "model", "brand", "system", "deviceId", "platform", "code", "openId", "unionid"].indexOf(g) || l.test(p[g])) {
continue;
}
d += p[g];
}
d += i,
d = (d = (d = (d = (d = (d = (d = (d = (d += s).replace(/\s+/g, "")).replace(/\n+/g, "")).replace(/\r+/g, "")).replace(/</g, "")).replace(/>/g, "")).replace(/&/g, "")).replace(/-/g, "")).replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, ""),
d = encodeURIComponent(d);
return {
md5: d = c.a.hexMD5(d),
tstr: i,
iArrStr: r && 0 < r.length ? r.join("_") : ""
}
}
简单来说就是从这 62
个字符中随机取出 20
个,得到键和值(这里我只关注了签到和签退所需要的代码片段,其他代码片段用途暂未了解)
5bfAJQgalpsqH4LQg16QZvwbce22mlEgGHIrosd57xtJSTFvw4890KE340mr
得到的结果如下
// 随机字符的下标
r: ['29','7','13','2','34','36','60','8','55','44','3','19','53','59','48','58','40','45','27','1']
// 随机字符
s: la4fIoil3SAQKrwm7T2b
i: 当前时间戳
d: adcode,address,clockStatus,punchInStatus,traineeId,当前时间戳,随机字符串s按顺序拼接后进行url编码然后md5加密
知道了加密逻辑,直接根据代码逻辑写成Python版
def get_sign_header(data: dict):
re_punctuation = re.compile("[`~!@#$%^&*()+=|{}':;,\\[\\].<>/?!¥…()—【】‘;:”“’。,、?]")
cookbook = ["5", "b", "f", "A", "J", "Q", "g", "a", "l", "p", "s", "q", "H", "4", "L", "Q", "g", "1", "6",
"Q",
"Z", "v", "w", "b", "c", "e", "2", "2", "m", "l", "E", "g", "G", "H", "I", "r", "o", "s", "d",
"5",
"7", "x", "t", "J", "S", "T", "F", "v", "w", "4", "8", "9", "0", "K", "E", "3", "4", "0", "m",
"r",
"i", "n"]
except_key = ["content", "deviceName", "keyWord", "blogBody", "blogTitle", "getType", "responsibilities",
"street", "text", "reason", "searchvalue", "key", "answers", "leaveReason", "personRemark",
"selfAppraisal", "imgUrl", "wxname", "deviceId", "avatarTempPath", "file", "file", "model",
"brand", "system", "deviceId", "platform", "code", "openId", "unionid"]
noce = [random.randint(0, len(cookbook) - 1) for _ in range(20)]
now_time = int(time.time())
sorted_data = dict(sorted(data.items(), key=lambda x: x[0]))
sign_str = ""
for k, v in sorted_data.items():
v = str(v)
if k not in except_key and not re.search(re_punctuation, v):
sign_str += str(v)
sign_str += str(now_time)
sign_str += "".join([cookbook[i] for i in noce])
sign_str = re.sub(r'\s+', "", sign_str)
sign_str = re.sub(r'\n+', "", sign_str)
sign_str = re.sub(r'\r+', "", sign_str)
sign_str = sign_str.replace("<", "")
sign_str = sign_str.replace(">", "")
sign_str = sign_str.replace("&", "")
sign_str = sign_str.replace("-", "")
sign_str = re.sub(f'\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]', "", sign_str)
sign_str = quote(sign_str)
sign = hashlib.md5(sign_str.encode('ascii'))
return {
"n": ",".join(except_key),
"t": str(now_time),
"s": "_".join([str(i) for i in noce]),
"m": sign.hexdigest(),
"v": "1.6.36"
}
结果
首次签到
签退
声明
本记录仅供学习参考
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 途深
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果