起因
像往常一样没啥事查看青龙日志的时候发现脚本报错了。
一开始还以为是网络问题没管他,等练车回来的时候看了一眼发现还是报错,仔细看了看发现是加了验证的参数
经过
打开Fiddler,开始抓包。
随便点点,看看请求参数有没有什么变化。
这里可以明显看出比之前多了3个参数 timestamp
nonce
signature
而且在每次的请求时都会通过/public/api/getServerTimestamp
获取一下服务器的时间戳 。
先重放一次试试....
居然获取成功,那这不简单了,把原来脚本的请求再加上这三个参数不就行了。
不能这么简单吧,再重放一次试试...
果然...是我想的太简单了,看样子经过一定的时间那几个参数会过期,貌似是20秒左右。
那我手快点,生成一个现在的时间戳然后Get一下看看会怎么样。
不出所料依旧是 {"code":802,"message":"sign error3"}
看来不得不琢磨琢磨另外两个参数了。
开始分析参数的生成
因为服务器就传过来一个时间戳,那两个参数应该就是本地的JS生成的,不出意外只要知道生成的规则,应该就能解决了。
把这个网页全部的 JS 文件下载下来,然后搜索 timestamp
nonce
signature
这几个参数,发现在 index.d774f.js
这个文件的 549-590 行出现了很多次。那应该就是这里了。
分析
可以看出这里有对变量赋值的操作。
先来看看 signature
它通过 xysz.encrypt.Md5.encode(o).toUpperCase()
赋值。
.toUpperCase()
是转换成大写,xysz.encrypt.Md5.encode(o)
看样子是他自己写的函数应该和 Md5 有关,传递的参数是 o 。
那再往上看看 o 是怎么来的
o 的值又是通过 xysz.tool.getSortedQuery(a)
赋值 ,参数是 a ,那再往上看 a 是怎么来的。
a = {
clientKey: e[t].clientKey,
clientSecret: e[t].clientSecret,
timestamp: i.default.ServerTimestamp,
nonce: n
},
clientKey
clientSecret
和 e 有关,timestamp
应该就是前面服务器传的时间戳,nonce
等于 n 的值。
还要再往上看......
n = this.randomString()
n 的值和randomString
函数有关,通过搜索在当前的文件找到了这个函数。
e.prototype.randomString = function (e) {
void 0 === e && (e = 16);
for (var t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678", n = t.length,a = "", r = 0; r < e; r++) a += t.charAt(Math.floor(Math.random() *n));
return a
},
可以看出是随机生成一个长度为16的字符串,那看来nonce
的值就是一个随机字符串了。
还剩下一个 signature
,前面说到 clientKey
clientSecret
和 e 的值有关,而关于e 的值是个对象又和 t 有关,没办法再去找 t 的值。
t = xysz.saas.getEnvByHostname()
t 又是一个函数赋的值。
在 xysz.min.8ac30.js
文件发现了这个函数
t.prototype.getEnvByHostname = function() {
return ["devapi.xiaoyisz.com", "qiehuangdev.ioutu.cn", "localhost"].includes(location.hostname) ?
"dev" : "prod"
}
发现是一个三元运算符,拿到正确的值后回到对象 a,e[t].clientKey
e[t].clientSecret
的值就显而易见了。
吐槽一句...这不管三元运算符的结果是啥 e[t].clientKey
e[t].clientSecret
这两个值的结果一样啊。
a的值有了,扔到 xysz.tool.getSortedQuery(a)
函数里取到值。
如法炮制得到 getSortedQuery
t.getSortedQuery = function(t) {
var e = Object.keys(t).sort(),
n = "";
return e.forEach(function(e) {
if (t[e] || 0 === t[e]) {
var o;
o = t[e] instanceof Object ? e + "=" + JSON.stringify(t[e]) : e + "=" + t[e], n &&
o && (n += "&"), n += o
}
}), n
},
作用就是把 a 格式化输出,赋值给 o
'clientKey=xxx&clientSecret=xxx&nonce=xxx×tamp=xxx'
有了 o 后,回到最初的起点
signature: xysz.encrypt.Md5.encode(o).toUpperCase(),
经过测试,是把o的值转换成32位 Md5 后再变成大写。
那这样,signature
的值就很明确了。
到此,对JS加密的分析结束了。改改之前的脚本,把那几个参数加上去,脚本又能正常运行啦!