起因与目标
最近想搞个自己的小程序,觉得最实用的就是去水印小程序,可以下载抖音、小红书的无水印视频和图片。要做这个,怎么去水印是关键。一种方法就是深究底层原理自己搞,这种对不是专业做逆向的我来说太困难了。还有一种就是找一个去水印接口,发送链接,返回无水印视频图片的地址。然后我在网上搜了一圈,去水印接口基本都是收费,而且你想买还不一定找得到卖的人。于是我尝试着反编译某个去水印小程序,看看能不能拿到它的接口给我自己用。事实后来证明是行不通的,后面我会说明。
反编译小程序
这一步我是参考的这篇文章微信小程序反编译~2022年
按照他的步骤来基本没什么问题,唯一需要注意的是在文件管理中打开的文件夹下面并没有小程序的缓存,而是在上一级,比如这是我的路径
然后就用工具反编译就行了,得到反编译之后的代码。
源码查看
使用微信开发者工具导入源码。导入代码的时候,这里的AppID我用了我自己的AppID,这是一个大坑,后面会提到。
导入之后我显示尝试了一下,在开发者工具中粘贴地址解析无水印视频,结果不行。
可以看到返回的code是7015,正确返回应该是0。
于是我去研究了一下这个请求方法
formSubmit: function(t) {
var a = this, e = t.detail.value.txtUrl;
a.startSubmit(e);
},
startSubmit: function(t) {
this.startGetData(t);
},
startGetData: function(t) {
var a = this, e = t && t.indexOf("http");
if (t && -1 != e) {
t = t.substring(e);
var n = i.getWxCode();
console.log(n), "" != n && null != n ? a.getVideoInfo(t, n) : wx.login({
success: function(e) {
e.code && (i.setWxCode(e.code), a.getVideoInfo(t, e.code));
}
});
} else a.setData({
btnSubmitEnable: !0
}), wx.showToast({
title: "请输入短视频分享链接地址",
icon: "none",
duration: 2e3
});
},
getVideoInfo: function(a, e) {
var n = this;
n.setData({
btnSubmitEnable: !1,
btnSubmitLoading: !0,
showVideo: !1,
loadModal: !0
}), wx.showLoading({
title: "加载中..."
});
var o = {
text: a,
code: e
};
t.formParmas(o), wx.request({
url: i.getApiDomain() + "/Video/GetVideoInfo",
method: "post",
data: o,
timeout: 2e4,
header: {
"content-type": "application/x-www-form-urlencoded",
DevINfo: t.getDevInfo()
},
success: function(t) {
if (0 == t.data.code) wx.setStorage({
key: "video",
data: t.data.data
}), wx.navigateTo({
url: "/pages/index/video/index"
}), n.setData({
textVal: "",
showCopy: !0
}); else {
var a = t.data.msg;
7015 != t.data.code && 7e3 != t.data.code || (i.setWxCode(""), a = "服务器繁忙,请重试!"),
wx.showModal({
title: "提示",
confirmText: "确定",
showCancel: !1,
content: a,
success: function(t) {}
});
}
},
fail: function(t) {
wx.showModal({
title: "提示",
confirmText: "确定",
showCancel: !0,
content: "请求失败,请检查网络是否正常,或者切换网络重试一下。",
success: function(t) {
t.confirm;
}
});
},
complete: function(t) {
n.setData({
btnSubmitEnable: !0,
btnSubmitLoading: !1,
loadModal: !1
}), wx.hideLoading();
}
});
}
因为我对js代码不太熟悉,所以这里借用了一下chatgpt帮我解释(有一说一chatgpt是真的好用)
根据代码和chatgpt的解释,我来总结一下:
GetVideoInfo
这个接口需要5个参数,其中三个参数text是链接地址,versionCode是小程序版本,appId是开发者的appId,都是固定的。唯一不同的是code和sign。而sign是其余几个参数的key和value再加上md5的算法生成的,只要code能确定,sign就能确定。所以关键是code。- code是微信登录凭证,通过调用
getWxCode
方法获取。如果code不为空就当做参数发起请求;如果为空就调用wx.login
方法获取,并通过setWxCode
方法放入本地缓存。 getWxCode
会调用两个带有固定前缀的方法getWxCodeKey
和getWxCodeTimeKey
来获取存储在本地的微信登录凭证码以及其对应的时间戳。接着,函数判断当前时间戳与本地存储的时间戳之差是否超过300秒(5分钟),如果是,则说明微信登录凭证码已过期,函数返回一个空字符串;否则,函数直接返回本地存储中的微信登录凭证码。
寻找原因
所以为什么我本地调试生成的code无效呢?我开始查找资料,以下是相关的资料:
【微信小程序】详解微信小程序登录wx.login和获取用户信息wx.getUserInfo
下面是我总结出来的结论:
- wx.login方法是静默的,不会弹出授权弹窗。该方法返回一个code,也就是用户登录凭证,有效期5分钟。
- 后台拿到code之后,可以调用
code2Session
方法,获取openid(用户唯一标识)等参数。 - 每次调用解析接口
GetVideoInfo
的时候,会将code传到后端,后端对code进行校验。校验通过再执行业务逻辑。怎么校验呢?我这里随便设想一种方案:以code为key去redis中查找有没有相关的键值对,如果有就校验通过;如果没有就调用code2Session
方法,调用失败就认为code校验失败;否则说明code校验通过,并且以code为key, openid为value存入redis中,并设置过期时间为5分钟。 - 小程序分发的code是与appid绑定的,仅当前申请code的appid才可使用
最关键的一点,wx.login返回的code与appid是绑定的,这也是为什么我会调用失败的原因。code2Session
需要4个请求参数,小程序appId,小程序appSecret,wx.login返回的code,grant_type(常量值)。因为我在导入代码的时候,在开发者工具里设置的appId是我自己的appId,所以生成的code也和我的appId是绑定的,因此在调用code2Session
方法的时候必然不通过。一句话,开发者工具中设置的appId和接口后端中的appId必须相同。
那我能不能修改开发者工具中的appId呢?答案是不能,因为需要开发者的微信登录
那我能不能抓包,抓一个可以通过的code,写死到程序中?答案是可以,但是5分钟之后就会失效,原因我上面已经讲了。
原理图
按照逻辑我画了一张原理图,应该能把过程表达清楚了
总结
虽然折腾的过程失败了,但我也不是没有收获。以后做自己的项目的时候就可以用这套机制来防止别人盗用我的接口。
btw我还是折腾点更有价值的事情吧。