前言
做工程总是需要绘制一些状态图、UML图之类的,在线比较好用的比如ProcessOn,Rappid的作用,就是方便开发这么一类的前端绘图框架,但是,免费试用的审批太慢了,然后,每次打开,都会有一个弹窗,所以就有Motivation,逆向源码,取消弹窗。
尝试
官网试用申请了两天没反应,从github上找到了别人上传的3.2.0的试用版。首先定位目标文件,惯例,rappid.js,这是这个项目的精华所在,别的地方都是明码,只有这里进行了js混淆压缩。
- window.alert,既然是浏览器弹窗,首先搜索这个方法,不出所料,没有;
- base64,既然搜不到,肯定是源码被加密了,然后通过反射调用的,那就查找加密后的字符串,前端加密常见的base64,特征就是结尾的“=”,还有atob方法,找到了;
var El,
Ll = ["TyfCoTnDj8Opw6A=", "wp5fA8Ouw48=", "w7zDnkvDnMObwq9aazrCvMK3CT0dMMOrHMKAwrPCgsKGDgfDo8K5EMKVZ8OrwpLCr8KgNgANCcKxw6vCrVHDtDrChl5fKMOwNBLDhMOaGcOuwodPwr3DmSrDjcO+cGzCrMK6w7HCt8KTwpVRwr0EwoHDsxx0W1TDmkA2DcKsDQLDg8KBw4BwwrjDsUlYwo0=", "Z2zDnlEQUEwSwqg=", "W0bDjgfCt8KTw4k=", "QMOgw5zDi8O+bMKuw6s7WQ==", "B8Oow6XDvgI2DUHDqcKJwrFDMw=="];
El = Ll, function (t) {
for (; --t;) El.push(El.shift())
}(170);
var Dl, Gl, zl, Bl = function (t, e) {
var i = Ll[t -= 0];
if (void 0 === Bl.JbcFeE) {
!function () {
var e;
try {
e = Function('return (function() {}.constructor("return this")( ));')()
} catch (t) {
e = window
}
e.atob || (e.atob = function (t) {
for (var e, i, n = String(t).replace(/=+$/, ""), r = "", o = 0, s = 0; i = n.charAt(s++); ~i && (e = o % 4 ? 64 * e + i : i, o++ % 4) && (r += String.fromCharCode(255 & e >> (-2 * o & 6)))) i = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(i);
return r
})
}();
Bl.GdaSQk = function (t, e) {
for (var i, n, r = [], o = 0, s = "", a = "", l = 0, h = (t = atob(t)).length; l < h; l++) a += "%" + ("00" + t.charCodeAt(l).toString(16)).slice(-2);
for (t = decodeURIComponent(a), n = 0; n < 256; n++) r[n] = n;
for (n = 0; n < 256; n++) o = (o + r[n] + e.charCodeAt(n % e.length)) % 256, i = r[n], r[n] = r[o], r[o] = i;
for (var c = o = n = 0; c < t.length; c++) o = (o + r[n = (n + 1) % 256]) % 256, i = r[n], r[n] = r[o], r[o] = i, s += String.fromCharCode(t.charCodeAt(c) ^ r[(r[n] + r[o]) % 256]);
return s
}, Bl.dBQYqP = {}, Bl.JbcFeE = !0
}
var n = Bl.dBQYqP[t];
return void 0 === n ? (void 0 === Bl.IYWyxg && (Bl.IYWyxg = !0), i = Bl.GdaSQk(i, e), Bl.dBQYqP[t] = i) : i = n, i
};
Dl = new Date(parseInt(Bl("0x5", "Rs^U"))), Gl = parseInt(Bl("0x4", "b@rV")), zl = Bl("0x1", "koGJ"), Bl("0x2", "Xmd8") != typeof window && Gl && !isNaN(Gl) && Dl && !isNaN(Dl[Bl("0x3", "#KwN")]()) && Dl[Bl("0x6", "KlGK")]() + Gl - (new Date).getTime() <= 0 && window[Bl("0x0", "u$Jv")](zl)
处理
对这串混淆压缩后得代码进行复原,主要功能是一个有效期得判定,超过有效期,弹出对话框提醒。
let encrypt_list = ["TyfCoTnDj8Opw6A=",
"wp5fA8Ouw48=",
"w7zDnkvDnMObwq9aazrCvMK3CT0dMMOrHMKAwrPCgsKGDgfDo8K5EMKVZ8OrwpLCr8KgNgANCcKxw6vCrVHDtDrChl5fKMOwNBLDhMOaGcOuwodPwr3DmSrDjcO+cGzCrMK6w7HCt8KTwpVRwr0EwoHDsxx0W1TDmkA2DcKsDQLDg8KBw4BwwrjDsUlYwo0=",
"Z2zDnlEQUEwSwqg=",
"W0bDjgfCt8KTw4k=",
"QMOgw5zDi8O+bMKuw6s7WQ==",
"B8Oow6XDvgI2DUHDqcKJwrFDMw=="];
for (let i = 1; i > 0; i--) {
encrypt_list.push(encrypt_list.shift())
}
decoder = {
result: {},
atob: function (input) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
let str = String(input).replace(/=+$/, ""), output = "", bs, buffer;
for (let bc = 0, idx = 0; idx < str.length; idx++) {
buffer = chars.indexOf(str.charAt(idx));
if (~buffer) {
bs = bc % 4 ? 64 * bs + buffer : buffer;
if (bc++ % 4) {
output += String.fromCharCode(255 & bs >> (-2 * bc & 6))
}
}
}
return output;
},
decode: function (input, e) {
input = this.atob(input);//base64解码
let t = "", result = "";
for (let i = 0; i < input.length; i++) {
t += "%" + ("00" + input.charCodeAt(i).toString(16)).slice(-2);
}
input = decodeURIComponent(t);
let table = [];
for (let i = 0; i < 256; i++) {
table[i] = i;
}
for (let i = 0, j = 0; i < 256; i++) {
j = (j + table[i] + e.charCodeAt(i % e.length)) % 256;
const tmp = table[i];
table[i] = table[j];
table[j] = tmp;
}
for (let i = 0, j = 0, k = 1; i < input.length; i++, k = (k + 1) % 256) {
j = (j + table[k]) % 256;
const tmp = table[k];
table[k] = table[j];
table[j] = tmp;
result += String.fromCharCode(input.charCodeAt(i) ^ table[(table[k] + table[j]) % 256]);
}
return result
},
get: function (id, key) {
id -= 0;
if (undefined === this.result[id]) {
this.result[id] = this.decode(encrypt_list[id], key);
}
return this.result[id];
}
};
const record_date = new Date(parseInt(decoder.get("0x5", "Rs^U")));
const m_alert = decoder.get("0x0", "u$Jv"),
message = decoder.get("0x1", "koGJ"),
m_getTime1 = decoder.get("0x3", "#KwN"),
trial_time = parseInt(decoder.get("0x4", "b@rV")),
m_getTime2 = decoder.get("0x6", "KlGK");
console.log(trial_time);
decoder.get("0x2", "Xmd8") != typeof window &&
trial_time &&
!isNaN(trial_time) &&
record_date &&
!isNaN(record_date[m_getTime1]()) &&
record_date[m_getTime2]() + trial_time - (new Date).getTime() <= 0 &&
window[m_alert](message);
在rappid.js中,将这段代码全注释掉,就不会再出现提醒啦。
总结
总体,rappid的加密还不是很复杂,也许rappid的厂商和大多数软件公司一样,卖的不是产品,而是维护,以及计算资源。