reqable抓包我的-签到页面
找到签到接口地址,https://user-api.smzdm.com/checkin,看到请求参数

多次抓包,发现sign和time会变化,weixin,f,v,sk,token都是固定值,而captcha和touchstone_event始终为空
JADX打开apk,字符串搜索”sign”
可以看到一些sdk中的类,和一个比较重要的p192Hf.C1998a,
这个被混淆过的方法进去之后就能看到签到接口传的f,v,weixin,time一些参数了

sign这个值由m7143c生成,看一下它的逻辑

可以看到这里先进行了参数拼接 + &key= + key值,再传入C18324p0.m83928a()做某种加密,最后结果转大写返回
我们先看ZDMKeyUtil类

大概是加载了一个key,回去看C18324p0.m83928a这个方法
可以看到是一个MD5,所以现在大概了解流程了
-
p192Hf.C1998a 接收Map(签到请求的所有参数)
- 对Map按key排序,遍历拼接成字符串
- 末尾拼接 &key= + ZDMKeyUtil.m83611a().m83612b()获取的key值
- 去空格.replaceAll(StringUtils.SPACE, “”)
- 传入
C18324p0.m83928a() 进行MD5加密,得到32位小写十六进制字符串
- 转大写
- 返回最终的sign值
那么接下来需要得到加密前的参数并且复写加密算法
可以用Xposed和Frida都可以,我以Xposed举例
1.hook p192Hf.C1998a这个类中的m83928a方法,获取传入的map和sign值,因为也还有其他接口调用了这个方法,因此对比Reqable抓包抓到的参数和sign值,确定是否是我们需要找的签到接口
2.hook ZDMKeyUtil这个类中的方法,获取最终拼接在后面的key参数
3.hook m83928a方法,获取加密前的字符串和加密后的值,加密后转为32位大写即为sign值
Xposed代码供参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| package com.hope.smzdm;
import java.util.Map;
import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class TestHook implements IXposedHookLoadPackage{ @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) { if (loadPackageParam.packageName.equals("com.smzdm.client.android")) { XposedHelpers.findAndHookMethod("Hf.a", loadPackageParam.classLoader, "c", Map.class, String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { String str = param.getResult().toString(); String param1 = param.args[0].toString(); String param2 = param.args[1].toString(); XposedBridge.log("hook success1----" + str); XposedBridge.log("hook success2----" + param1); XposedBridge.log("hook success3----" + param2); } });
XposedHelpers.findAndHookMethod("com.smzdm.client.base.utils.ZDMKeyUtil", loadPackageParam.classLoader, "a", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { String str = param.getResult().toString(); XposedBridge.log("hook success4----" + str); } });
XposedHelpers.findAndHookMethod("com.smzdm.client.base.utils.ZDMKeyUtil", loadPackageParam.classLoader, "b", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { String str = param.getResult().toString(); XposedBridge.log("hook success5----" + str); } });
XposedHelpers.findAndHookMethod("com.smzdm.client.base.utils.p0", loadPackageParam.classLoader, "a", String.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { String str = param.getResult().toString(); String param1 = param.args[0].toString(); XposedBridge.log("hook success6----" + param1); XposedBridge.log("hook success7----" + str); } }); } } }
|
代码中的类名和方法名与jadx中不同是因为jadx在反混淆时自动将一些较为简单的常见方法及类名翻译,如将a翻译为m83928a
那么右键需要的名称复制为Xposed代码即可看到真实名称
运行该模块可以看到日志中成功hook到了加密前的字符串和key值,这个key是不变的

那么现在已知密文和加密算法,我们用python模拟一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import time import random import hashlib import requests
def md5(rawstr): hl = hashlib.md5() hl.update(rawstr.encode(encoding='UTF-8')) return hl.hexdigest().upper()
def checkin(cookie): signurl = "https://user-api.smzdm.com/checkin" zdmkey = 'apr1$AwP!wRRT$gJ/q.X24poeBInlUJC' zdmsk = 'qMeb6cxJeDmCCME/m6lszTvcaBy1NzXPXwq//rf6nPjKawjsKMFBcVLEblpieutv' zdmtoken = cookie[5:] timestamp = int(time.time()) rawdata = f'f=android&sk={zdmsk}&time={timestamp}000&token={zdmtoken}&v=9.9.12&weixin=1&key={zdmkey}' sign = md5(rawdata) formdata = { "sk": zdmsk, "sign": sign, "weixin": "1", "v": "9.9.12", "captcha": "", "f": "android", "token": zdmtoken, "touchstone_event": "", "time": f"{timestamp}000" } headers = { "User-Agent": 'smzdm_android_V11.1.75 rv:1175 (PJF110;Android14;zh)smzdmapp', "Cookie": cookie, "Accept-Encoding": 'gzip', "Connection": "Keep-Alive", "request_key": f"{random.randint(10000000, 99999999)}{timestamp}", } resp = requests.post(signurl, headers=headers, data=formdata).json() if resp["error_code"] == '0': resp_data = resp["data"] checkin_num = resp_data["daily_num"] gold = resp_data["cgold"] silver = resp_data["pre_re_silver"] point = resp_data["cpoints"] exp = resp_data["cexperience"] rank = resp_data["rank"] cards = resp_data["cards"] msg = f"""🏆签到成功\n🏆已连续签到{checkin_num}天\n🏆等级{rank}\n🏆补签卡{cards}\n🏆碎银{silver}\n🏆金币{gold}\n🏆积分{point}\n🏆经验{exp}""" print(msg) else: msg = f'签到失败!失败原因:{resp["error_msg"]}' print(msg)
if __name__ == "__main__": zdmcookie = "partner_name=oppo;sess=BB-13u1HEgMYkL85XUyVkvw1OaFEYNYFIfmmzdujmKE31N1WvuKsfNQZ1wEPChUpmhAjxw9xvBcBnC7LEq1erbW3RlTnYg%3D;pid=KSRkj6cIVJ9SORMRQPiNNlmZlM%2BKqOQhQuAK3SmTbsFzbfQDpoHn1A%3D%3D;device_type=OnePlusPJF110;basic_v=0;client_id=5fa70c5c0f44f42fe06d8449f321f14d.1779105225256;pr_new_device_id=5g1eic4c332b4fd1b3e5b25ciif1daa2;network=1;device_system_version=14;partner_id=16;user_type=2;device_smzdm=android;apk_partner_name=oppo;smzdm_id=5149224266;pr_device_s=5g1eic4c332b4fd1b3e5b25ciif1daa2;device_push=1;f=android;pr_z_dr=5g1eic4c332b4fd1b3e5b25ciif1daa2;apk_partner_id=16;session_id=5fa70c5c0f44f42fe06d8449f321f14d.1779105225337;device_rid=5fa70c5c0f44f42fe06d8449f321f14d;active_time=1779105227;z_ai=3c4946a54e49723fdaec9c952b2d373e;v=11.1.75;pr_device_id=5g1eic4c332b4fd1b3e5b25ciif1daa2;last_article_info=;device_recfeed_setting=%7B%22haojia_recfeed_switch%22%3A%221%22%2C%22homepage_sort_switch%22%3A%221%22%2C%22other_recfeed_switch%22%3A%221%22%2C%22shequ_recfeed_switch%22%3A%221%22%7D;device_smzdm_version_code=1175;" checkin(zdmcookie)
|
预期结果如下
1 2 3 4 5 6 7 8
| 🏆签到成功 🏆已连续签到3天 🏆等级0 🏆补签卡0 🏆碎银0 🏆金币0 🏆积分0 🏆经验0
|
基于这个脚本,我们可以再加入类如云函数,Win任务计划程序,Linux/Mac 服务器 Cron 任务,青龙面板等系列的东西实现完全自动化
而加入更多的cookie可以实现多账户批量签到
集成 PushPlus、Server 酱、钉钉机器人等,可以实现签到结果自动通知
那么以上就是签名算法的发现过程和自动签到的实现