前言
前几天发了一个某付宝小程序的sign参数md5加密拿到明文参数的帖子…
又发现一个别的小程序,好像是用的对称加密,耐不住好奇心,就试了试…结果成功实现了加解密的操作。遂发帖记录一下。
工具
- fiddler【抓包工具】
- Drony【转发工具,因为fiddler抓不到小程序的包,所以用Drony转发用fiddler能抓到包】
- MT(或NP)管理器【文件管理】
- Nodepad++【JS格式化+分析】
开始操作
Drony的配置和开启
之前就已经讲过了,就不再讲解了。想了解的可以跳转看一下:fiddler+drony抓包
拿到源码
用MT管理器在/data/user/0/com.eg.android.AlipayGphone/files/nebulaInstallApps/
目录中直接搜索小程序的名字,找到小程序的目录
[ps:随意修改或者用别的小程序的json文件替换改小程序的文件,再次打开小程序的时候会加载网页,通常情况下index.worker.js就是小程序的主要代码逻辑]
操作+抓包
小程序端操作:输入手机号,点击获取验证码,FD搜到smsitfs/sendSms的抓包信息
密文为aNrKSihAtEQuPZr8qdxkxspWvzMjXwvunyoWxdKdqj8HPdxkxsxjtwrdtwYMnic8nwv4vzx1nwMuty7NnwqWOZndOsqjxHJ=
请求密文JS加密分析
sendSms接口分析
JS中搜索smsitfs/sendSms
,找到变量sendSms
sendSms: i.default.BASE_QUANGUO_URL + "smsitfs/sendSms",
全词匹配搜索sendSms,发现有4处调用位置,且均为getCode的函数名,看名字就知道我们是找对了,取出其中一个并小小的分析一下
getCode: function () {
this.countDown();
var e = {
phoneNumber: this.data.phoneNumber
};
e.sign = f.default.sortAndStringfy(e, !0);//e的值{"phoneNumber": "183xxxx548", sign: "MD5操作之后的值"}
(0, l.default)(c.default.sendSms, {
isStr: !0,
data: f.default.transpositionEncryptNew((0, r.default)({}, e), !1)
}).then(function (e) {
var t = f.default.transpositionDecryptNew(e);
if (t) {
var a = JSON.parse(t);
console.log(a),
1 === a.retCode ? (0, o.default)("获取成功!", "success") : (0, o.default)(a.msg)
}
})
},
可得出流程:
先分析获取e.sign的sortAndStringfy函数
搜索sortAndStringfy:到
t.default = {
transpositionEncryptNew: ae,
transpositionDecryptNew: ie,
sortAndStringfy: re
},
e.exports = t.default
加密函数transpositionEncryptNew=ae
解密函数transpositionDecryptNew=ie
也就是re函数就是sortAndStringfy函数,如下。
function re(e, t) {//根据传参可得e={'phoneNumber':'手机号'} , t = !0;
var a = new Array,
n = Object.keys(e);
n.sort();
for (var i = 0, r = n.length; i < r; i++) {
var o = e[n[i]],
s = [n[i]] + "=" + o;
a.push(s)
}
return t ? y.default.hexMD5(C.Base64.encode("&" + a.join("&"))) : a.join("&") + "&key=" + L
}
果然是MD5加密,枚举变量e,a = [value + “=” + 手机号],re函数的返回值为md5(base64编码("&" + a.join("&")))
再分析下加密ae函数
拿到sign的加密方法之后,赋值e.sign后拿到完整的参数e:{“phoneNumber”: “手机号”, sign: “re操作之后的值”}
ae函数
搜索function ae(
,拿到ae函数:
function ae(e, t) {
if (t) {
var a = JSON.stringify(e);
return X(G(), a)
}
var n = C.Base64.encode(JSON.stringify(e));//Base64编码
return X(G(), n)
}
函数X和函数G
发现一个函数X和函数G
分别搜索function X(
和function G(
,拿到X和G的函数:
function X(e, t) {
for (var a = Z(t), n = new Array, i = 0; i < a.length; i++) {
var r = e.get(a[i]);
r ? n.splice(i, 1, r) : n.splice(i, 1, a[i])
}
return ee(n)
}
function G() {
var e = new Map,
t = Z(U),
a = Z(q),
n = t.length;
t.length != a.length && console.log("密钥和原字符长度不一致!");
for (var i = 0; i < n; i++)
e.set(t[i], a[i]);
return e
}
函数ee和函数Z
又发现一个函数ee和函数Z,还有变量U和q
继续搜索搜索function ee(
和function Z(
,拿到ee和Z的函数:
function Z(e) {
var t,
a = new Array,
n = void 0;
t = e.length;
for (var i = 0; i < t; i++)
(n = e.charCodeAt(i)) >= 65536 && n <= 1114111 ? (a.push(n >> 18 & 7 | 240), a.push(n >> 12 & 63 | 128), a.push(n >> 6 & 63 | 128), a.push(63 & n | 128)) : n >= 2048 && n <= 65535 ? (a.push(n >> 12 & 15 | 224), a.push(n >> 6 & 63 | 128), a.push(63 & n | 128)) : n >= 128 && n <= 2047 ? (a.push(n >> 6 & 31 | 192), a.push(63 & n | 128)) : a.push(255 & n);
return a
}
function ee(e) {
if ("string" === typeof e)
return e;
for (var t = "", a = e, n = 0; n < a.length; n++) {
var i = a[n].toString(2),
r = i.match(/^1+?(?=0)/);
if (r && 8 == i.length) {
for (var o = r[0].length, s = a[n].toString(2).slice(7 - o), l = 1; l < o; l++)
s += a[l + n].toString(2).slice(2);
t += String.fromCharCode(parseInt(s, 2)),
n += o - 1
} else
t += String.fromCharCode(a[n])
}
return t
}
变量U和q
U = “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890”,
q = “752wpRiLxrbDvnXgocTyEmZVOtSPqYalf9dsM8IH6GC0FeA3K1Nzuj4WQkBUhJ”;
顺便在附近还拿到了变量J和变量$…顺手牵羊吗?
整合加密代码并在浏览器中执行
由于代码中有md5和base64,而且base64解码用window.atob的时候有中文会乱码,
所以百度搜了一段md5函数和base64编码解码的函数,并替换小程序中的原方法。
执行var t={"phoneNumber":"183XXXX48"};t.sign=re(t,1);ae(t)
加密
结果为aNrKSihAtEQuPZr8qdxkxspWvzMjXwvunyoWxdKdqj8HPdxkxsxjtwrdtwYMnic8nwv4vzx1nwMuty7NnwqWOZndOsqjxHJ=
和fiddler的抓包结果是一样的,说明加密是有效的。
接口请求返回密文JS解密分析
和加密分析一样,之前我们得出解密函数transpositionDecryptNew=ie,依次找出用到的函数和变量
函数ie
function ie(e, t) {
if (t)
return X(Q(t), C.Base64.decode(e));
var a = X(Q(), e);
return C.Base64.decode(a)
}
函数X和函数Q
//函数X、函数Z、函数ee,变量U和q,刚才的加密已经有了,所以不再需要了
function Q(e) {
var t = new Map,
a = Z(e || U),
n = Z(q),
i = a.length;
a.length != n.length && console.log("密钥和原字符长度不一致!");
for (var r = 0; r < i; r++)
t.set(a[r], n[r]);
return t
}
整合代码,执行解密post返回密文
JS加解密源码下载地址
请使用AES/CBC/pkcs7/128位
q = "752wpRiLxrbDvnXgocTyEmZVOtSPqYalf9dsM8IH6GC0FeA3K1Nzuj4WQkBUhJ";
key=q.substr(1,16);iv=q.substr(16,16);
下载地址 :7arhyjZbN3nihvyFZtrJd+N8xXMNL2Ckx4STQfESptLuLPjB/nSBphOi1legar9ZO9SD1fL5CcTshPIj6fdzDvBvy5KXS7x+hgzAA7yVL0o=