前言
目前,我们日常浏览的网站中,日常会有防止网站被黑客克隆网站、爬取网站、压缩代码等的需求,都会使用一些方法对源码进行处理。其中,有的使用Webpack对JavaScript代码进行打包,也有使用js代码加密的方法。因此,本篇文章将会对js代码加密进行一个简单的分析,并主要针对不可逆加密提供一些常用的近似解密思路。
JavaScript加密分析
首先,我们能确定的一件事是,无论加密与否,加载的js文件都能够在浏览器其中执行,并得到对应的结果。
有的思路是根据一些参数特殊处理进行加密,也有的是跟后端达成协议,进行加解密的方式得出结果并执行。但是,后者的方案会对我们的服务造成额外的压力,因此使用前者更为主流。因此本篇文章只讨论前者这种的方案。
近似解密的方案是基于代码逻辑正确、提高可读性的方向进行的。
这里列举了一些加密方式(可逆的):
- 最简单的加密解密
- 转义字符""的妙用
- 使用Microsoft出品的脚本编码器Script Encoder来进行编码
- 任意添加NUL空字符(十六进制00H)
- 无用内容混乱以及换行空格TAB大法
- 自写解密函数法
对应的加解密思路可以看这里,我就不一一列举了
不可逆的加密方式:
根据各种指定的参数混淆+不可逆的算法进行加密https://www.jsjiami.com/
但是,即使是不可逆加密过后,得出的结果也是能正常执行的,符合JavaScript的语法的,因此,我们就可以就此对源码进行翻译。
举个栗子并分享一个思路
举个栗子
var __encode = 'jsjiami.com', _a = {}, _0xb483 = ["\x5F\x64\x65\x63\x6F\x64\x65", "\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"]; (function (_0xd642x1) { _0xd642x1[_0xb483[0]] = _0xb483[1] })(_a); var __Oxb24bc = ["\x6C\x69\x74\x65\x2D\x61\x6E\x64\x72\x6F\x69\x64\x26", "\x73\x74\x72\x69\x6E\x67\x69\x66\x79", "\x26\x61\x6E\x64\x72\x6F\x69\x64\x26\x33\x2E\x31\x2E\x30\x26", "\x26", "\x26\x38\x34\x36\x63\x34\x63\x33\x32\x64\x61\x65\x39\x31\x30\x65\x66", "\x31\x32\x61\x65\x61\x36\x35\x38\x66\x37\x36\x65\x34\x35\x33\x66\x61\x66\x38\x30\x33\x64\x31\x35\x63\x34\x30\x61\x37\x32\x65\x30", "\x69\x73\x4E\x6F\x64\x65", "\x63\x72\x79\x70\x74\x6F\x2D\x6A\x73", "", "\x61\x70\x69\x3F\x66\x75\x6E\x63\x74\x69\x6F\x6E\x49\x64\x3D", "\x26\x62\x6F\x64\x79\x3D", "\x26\x61\x70\x70\x69\x64\x3D\x6C\x69\x74\x65\x2D\x61\x6E\x64\x72\x6F\x69\x64\x26\x63\x6C\x69\x65\x6E\x74\x3D\x61\x6E\x64\x72\x6F\x69\x64\x26\x75\x75\x69\x64\x3D\x38\x34\x36\x63\x34\x63\x33\x32\x64\x61\x65\x39\x31\x30\x65\x66\x26\x63\x6C\x69\x65\x6E\x74\x56\x65\x72\x73\x69\x6F\x6E\x3D\x33\x2E\x31\x2E\x30\x26\x74\x3D", "\x26\x73\x69\x67\x6E\x3D", "\x61\x70\x69\x2E\x6D\x2E\x6A\x64\x2E\x63\x6F\x6D", "\x2A\x2F\x2A", "\x52\x4E", "\x4A\x44\x4D\x6F\x62\x69\x6C\x65\x4C\x69\x74\x65\x2F\x33\x2E\x31\x2E\x30\x20\x28\x69\x50\x61\x64\x3B\x20\x69\x4F\x53\x20\x31\x34\x2E\x34\x3B\x20\x53\x63\x61\x6C\x65\x2F\x32\x2E\x30\x30\x29", "\x7A\x68\x2D\x48\x61\x6E\x73\x2D\x43\x4E\x3B\x71\x3D\x31\x2C\x20\x6A\x61\x2D\x43\x4E\x3B\x71\x3D\x30\x2E\x39", "\x75\x6E\x64\x65\x66\x69\x6E\x65\x64", "\x6C\x6F\x67", "\u5220\u9664", "\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A", "\u671F\u5F39\u7A97\uFF0C", "\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C", "\x6A\x73\x6A\x69\x61", "\x6D\x69\x2E\x63\x6F\x6D"]; function taskUrl(_0x7683x2, _0x7683x3 = {}) { let _0x7683x4 = + new Date(); let _0x7683x5 = `${__Oxb24bc[0x0]}${JSON[__Oxb24bc[0x1]](_0x7683x3)}${__Oxb24bc[0x2]}${_0x7683x2}${__Oxb24bc[0x3]}${_0x7683x4}${__Oxb24bc[0x4]}`; let _0x7683x6 = __Oxb24bc[0x5]; const _0x7683x7 = $[__Oxb24bc[0x6]]() ? require(__Oxb24bc[0x7]) : CryptoJS; let _0x7683x8 = _0x7683x7.HmacSHA256(_0x7683x5, _0x7683x6).toString(); return { url: `${__Oxb24bc[0x8]}${JD_API_HOST}${__Oxb24bc[0x9]}${_0x7683x2}${__Oxb24bc[0xa]}${escape(JSON[__Oxb24bc[0x1]](_0x7683x3))}${__Oxb24bc[0xb]}${_0x7683x4}${__Oxb24bc[0xc]}${_0x7683x8}${__Oxb24bc[0x8]}`, headers: { '\x48\x6F\x73\x74': __Oxb24bc[0xd], '\x61\x63\x63\x65\x70\x74': __Oxb24bc[0xe], '\x6B\x65\x72\x6E\x65\x6C\x70\x6C\x61\x74\x66\x6F\x72\x6D': __Oxb24bc[0xf], '\x75\x73\x65\x72\x2D\x61\x67\x65\x6E\x74': __Oxb24bc[0x10], '\x61\x63\x63\x65\x70\x74\x2D\x6C\x61\x6E\x67\x75\x61\x67\x65': __Oxb24bc[0x11], '\x43\x6F\x6F\x6B\x69\x65': cookie } } } (function (_0x7683x9, _0x7683xa, _0x7683xb, _0x7683xc, _0x7683xd, _0x7683xe) { _0x7683xe = __Oxb24bc[0x12]; _0x7683xc = function (_0x7683xf) { if (typeof alert !== _0x7683xe) { alert(_0x7683xf) }; if (typeof console !== _0x7683xe) { console[__Oxb24bc[0x13]](_0x7683xf) } }; _0x7683xb = function (_0x7683x7, _0x7683x9) { return _0x7683x7 + _0x7683x9 }; _0x7683xd = _0x7683xb(__Oxb24bc[0x14], _0x7683xb(_0x7683xb(__Oxb24bc[0x15], __Oxb24bc[0x16]), __Oxb24bc[0x17])); try { _0x7683x9 = __encode; if (!(typeof _0x7683x9 !== _0x7683xe && _0x7683x9 === _0x7683xb(__Oxb24bc[0x18], __Oxb24bc[0x19]))) { _0x7683xc(_0x7683xd) } } catch (e) { _0x7683xc(_0x7683xd) } })({})
首先
看到上面的内容,分析得出里面包含着\x5F\x64\x65\x63\x6F\x64\x65
、\u5220\u9664
这些特殊字符串,我们可以得出,里面的混淆会是进行过unicode和16进制的转换得出的,因此,我们可以先对此先进行一次转换,最终得出的结果是
var __encode = 'jsjiami.com',
_a = {},
_0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function(_0xd642x1) {
_0xd642x1[_0xb483[0]] = _0xb483[1]
})(_a);
var __Oxb24bc = ["lite-android&", "stringify", "&android&3.1.0&", "&", "&846c4c32dae910ef", "12aea658f76e453faf803d15c40a72e0", "isNode", "crypto-js", "", "api?functionId=", "&body=", "&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=", "&sign=", "api.m.jd.com", "*/*", "RN", "JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)", "zh-Hans-CN;q=1, ja-CN;q=0.9", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
function taskUrl(_0x7683x2, _0x7683x3 = {}) {
let _0x7683x4 = +new Date();
let _0x7683x5 = `${__Oxb24bc[0x0]}${JSON[__Oxb24bc[0x1]](_0x7683x3)}${__Oxb24bc[0x2]}${_0x7683x2}${__Oxb24bc[0x3]}${_0x7683x4}${__Oxb24bc[0x4]}`;
let _0x7683x6 = __Oxb24bc[0x5];
const _0x7683x7 = $[__Oxb24bc[0x6]]() ? require(__Oxb24bc[0x7]) : CryptoJS;
let _0x7683x8 = _0x7683x7.HmacSHA256(_0x7683x5, _0x7683x6).toString();
return {
url: `${__Oxb24bc[0x8]}${JD_API_HOST}${__Oxb24bc[0x9]}${_0x7683x2}${__Oxb24bc[0xa]}${escape(JSON[__Oxb24bc[0x1]](_0x7683x3))}${__Oxb24bc[0xb]}${_0x7683x4}${__Oxb24bc[0xc]}${_0x7683x8}${__Oxb24bc[0x8]}`,
headers: {
'Host': __Oxb24bc[0xd],
'accept': __Oxb24bc[0xe],
'kernelplatform': __Oxb24bc[0xf],
'user-agent': __Oxb24bc[0x10],
'accept-language': __Oxb24bc[0x11],
'Cookie': cookie
}
}
}(function(_0x7683x9, _0x7683xa, _0x7683xb, _0x7683xc, _0x7683xd, _0x7683xe) {
_0x7683xe = __Oxb24bc[0x12];
_0x7683xc = function(_0x7683xf) {
if (typeof alert !== _0x7683xe) {
alert(_0x7683xf)
};
if (typeof console !== _0x7683xe) {
console[__Oxb24bc[0x13]](_0x7683xf)
}
};
_0x7683xb = function(_0x7683x7, _0x7683x9) {
return _0x7683x7 + _0x7683x9
};
_0x7683xd = _0x7683xb(__Oxb24bc[0x14], _0x7683xb(_0x7683xb(__Oxb24bc[0x15], __Oxb24bc[0x16]), __Oxb24bc[0x17]));
try {
_0x7683x9 = __encode;
if (!(typeof _0x7683x9 !== _0x7683xe && _0x7683x9 === _0x7683xb(__Oxb24bc[0x18], __Oxb24bc[0x19]))) {
_0x7683xc(_0x7683xd)
}
} catch (e) {
_0x7683xc(_0x7683xd)
}
})({})
到此
我们大致上已把对应的内容简单地解密出来了。但是,目前而言,源码的可读性比较差,我们可以再观察一下。可以发现,在开头已定义了__Oxb24bc
这个数组变量,文中的部分变量也使用到数组中的内容,因此,我们可以根据这样转化一波。可以得出一下:
var __encode = 'jsjiami.com',
_a = {},
_0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function (_0xd642x1) {
_0xd642x1["_decode"] = "http://www.sojson.com/javascriptobfuscator.html"
})(_a);
var __Oxb24bc = ["lite-android&", "stringify", "&android&3.1.0&", "&", "&846c4c32dae910ef", "12aea658f76e453faf803d15c40a72e0", "isNode", "crypto-js", "", "api?functionId=", "&body=", "&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=", "&sign=", "api.m.jd.com", "*/*", "RN", "JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)", "zh-Hans-CN;q=1, ja-CN;q=0.9", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
function taskUrl(_0x7683x2, _0x7683x3 = {}) {
let _0x7683x4 = +new Date();
let _0x7683x5 = `lite-android&${JSON.stringify(_0x7683x3)}&android&3.1.0&${_0x7683x2}&${_0x7683x4}&846c4c32dae910ef`;
let _0x7683x6 = "12aea658f76e453faf803d15c40a72e0";
const _0x7683x7 = $.isNode() ? require("crypto-js") : CryptoJS;
let _0x7683x8 = _0x7683x7.HmacSHA256(_0x7683x5, _0x7683x6).toString();
return {
url: ""+`${JD_API_HOST}api?functionId=${_0x7683x2}&body=${escape(JSON.stringify(_0x7683x3))}&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=${_0x7683x4}&sign=${_0x7683x8}`+"",
headers: {
'Host': 'api.m.jd.com',
'accept': '*/*',
'kernelplatform': 'RN',
'user-agent': 'JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)',
'accept-language': 'zh-Hans-CN;q=1, ja-CN;q=0.9',
'Cookie': cookie
}
}
}
(function (_0x7683x9, _0x7683xa, _0x7683xb, _0x7683xc, _0x7683xd, _0x7683xe) {
_0x7683xe = 'undefined';
_0x7683xc = function (_0x7683xf) {
if (typeof alert !== _0x7683xe) {
alert(_0x7683xf)
};
console.log(console)
if (typeof console !== _0x7683xe) {
console.log(_0x7683xf)
}
};
_0x7683xb = function (_0x7683x7, _0x7683x9) {
return _0x7683x7 + _0x7683x9
};
_0x7683xd = _0x7683xb('删除', _0x7683xb(_0x7683xb('版本号,js会定', '期弹窗,'), '还请支持我们的工作'));
try {
_0x7683x9 = __encode;
if (!(typeof _0x7683x9 !== _0x7683xe && _0x7683x9 === _0x7683xb('jsjia', 'mi.com'))) {
_0x7683xc(_0x7683xd)
}
} catch (e) {
_0x7683xc(_0x7683xd)
}
})({})
其中,我们可以看到_0x7683xb
这个方法,其实实际上处理的操作是在做字符串拼接,对此可以简化整理一下,并得出:
var __encode = 'jsjiami.com',
_a = {},
_0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function (_0xd642x1) {
_0xd642x1["_decode"] = "http://www.sojson.com/javascriptobfuscator.html"
})(_a);
var __Oxb24bc = ["lite-android&", "stringify", "&android&3.1.0&", "&", "&846c4c32dae910ef", "12aea658f76e453faf803d15c40a72e0", "isNode", "crypto-js", "", "api?functionId=", "&body=", "&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=", "&sign=", "api.m.jd.com", "*/*", "RN", "JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)", "zh-Hans-CN;q=1, ja-CN;q=0.9", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
function taskUrl(_0x7683x2, _0x7683x3 = {}) {
let _0x7683x4 = +new Date();
let _0x7683x5 = `lite-android&${JSON.stringify(_0x7683x3)}&android&3.1.0&${_0x7683x2}&${_0x7683x4}&846c4c32dae910ef`;
let _0x7683x6 = "12aea658f76e453faf803d15c40a72e0";
const _0x7683x7 = $.isNode() ? require("crypto-js") : CryptoJS;
let _0x7683x8 = _0x7683x7.HmacSHA256(_0x7683x5, _0x7683x6).toString();
return {
url: ""+`${JD_API_HOST}api?functionId=${_0x7683x2}&body=${escape(JSON.stringify(_0x7683x3))}&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=${_0x7683x4}&sign=${_0x7683x8}`+"",
headers: {
'Host': 'api.m.jd.com',
'accept': '*/*',
'kernelplatform': 'RN',
'user-agent': 'JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)',
'accept-language': 'zh-Hans-CN;q=1, ja-CN;q=0.9',
'Cookie': cookie
}
}
}
(function (_0x7683x9, _0x7683xa, _0x7683xc, _0x7683xd, _0x7683xe) {
_0x7683xe = 'undefined';
_0x7683xc = function (_0x7683xf) {
if (typeof alert !== _0x7683xe) {
alert(_0x7683xf)
};
console.log(console)
if (typeof console !== _0x7683xe) {
console.log(_0x7683xf)
}
};
_0x7683xd = '删除版本号,js会定期弹窗,还请支持我们的工作';
try {
_0x7683x9 = __encode;
if (!(typeof _0x7683x9 !== _0x7683xe && _0x7683x9 === 'jsjiami.com')) {
_0x7683xc(_0x7683xd)
}
} catch (e) {
_0x7683xc(_0x7683xd)
}
})({})
对此,基本已经翻译完成,剩下的给这些_0x7683
开头的变量换个名字,让他看起来更加舒服,在这里,我就大致根据这些变量定义的数据起个名字,并简化一些累赘的代码~
Magic~
var __encode = 'jsjiami.com',
_a = {};
(function (_0xd642x1) {
_0xd642x1["_decode"] = "http://www.sojson.com/javascriptobfuscator.html"
})(_a);
var __Oxb24bc = ["lite-android&", "stringify", "&android&3.1.0&", "&", "&846c4c32dae910ef", "12aea658f76e453faf803d15c40a72e0", "isNode", "crypto-js", "", "api?functionId=", "&body=", "&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=", "&sign=", "api.m.jd.com", "*/*", "RN", "JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)", "zh-Hans-CN;q=1, ja-CN;q=0.9", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
function taskUrl(id, data = {}) {
let time = +new Date();
let form = `lite-android&${JSON.stringify(data)}&android&3.1.0&${id}&${time}&846c4c32dae910ef`;
let code = "12aea658f76e453faf803d15c40a72e0";
const sha = $.isNode() ? require("crypto-js") : CryptoJS;
let sign = sha.HmacSHA256(form, code).toString();
return {
url: ""+`${JD_API_HOST}api?functionId=${id}&body=${escape(JSON.stringify(data))}&appid=lite-android&client=android&uuid=846c4c32dae910ef&clientVersion=3.1.0&t=${time}&sign=${sign}`+"",
headers: {
'Host': 'api.m.jd.com',
'accept': '*/*',
'kernelplatform': 'RN',
'user-agent': 'JDMobileLite/3.1.0 (iPad; iOS 14.4; Scale/2.00)',
'accept-language': 'zh-Hans-CN;q=1, ja-CN;q=0.9',
'Cookie': cookie
}
}
}
(function (encode, alarm) {
alarm = function (msg) {
if (typeof alert !== 'undefined') {
alert(msg)
};
if (typeof console !== 'undefined') {
console.log(msg)
}
};
try {
encode = __encode;
if (!(typeof encode !== 'undefined' && encode === 'jsjiami.com')) {
alarm('删除版本号,js会定期弹窗,还请支持我们的工作')
}
} catch (e) {
alarm('删除版本号,js会定期弹窗,还请支持我们的工作')
}
})({})
就此,解密已经处理好了
或许,写到这里,会有很多朋友会问
Q:为什么_0x7683
开头的变量没不能直接解析出他具体的变量名是什么呢?
A:首先,这个变量的生成具体是根据哪些参数算出来的,我们不清楚,想要去解开他需要尝试各种组合,有可能有朋友说,一看就知道是进制转换出来的。这么想,没错,但是,具体是哪个进制,你还要不断的尝试,而且,其实这只是一个变量名,到最后也不会影响里面的逻辑。因此,为了高效而言,还是直接起一个新的名字就好了。(只是审查源码的逻辑,不要太纠结)
工具方法(网上)
下面这个方法可以直接把数组的内容在文中替换,并且,把xxx["yyy"]的格式转为xxx.yyy的格式。其中,__Oxb7f82
需要根据实际情况下的字符串数组对应的变量进行替换,以本文的栗子为例是__Oxb24bc
,其中code
的数据是对应需要解码的js数据。
console.log(code.replace(/__Oxb7f82\[0x[\da-f]+\]/g,a=>{
a=`"${eval(a).replace(/"/g,"\"")}"`;
console.log(a);
return a
}).replace(/\[s*['"][^"']+['"]\s*\]/g,a=>{
a=a.trim();
a=a.substring(2,a.length-2)
console.log(a);
return "."+a
}))
写到最后,这篇文章仅做技术分享,若涉及版权和信息安全问题,请联系小编会马上处理~