我們?cè)谏现艿奈恼轮幸环N奇特的 JAVAScript 編碼風(fēng)格:Get 一種可以用來(lái)裝逼的 JavaScript 編碼風(fēng)格,引起了廣大網(wǎng)友的熱議。
這是實(shí)際上屬于一種代碼混淆技術(shù),可以讓們的代碼更難閱讀和逆向,同時(shí)也能租網(wǎng)一些惡意爬蟲(chóng)和自動(dòng)化分析。天我就帶大家來(lái)看看還有哪些其他能讓 JavaScript 代碼變得難以分析的代碼混淆技術(shù)。
我們以下面這段代碼為例:
<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">console.log("ConardLi",666);</pre>
通過(guò)一些轉(zhuǎn)換,它可以變成下面這個(gè)樣子:
怎么做到的呢?我們一起來(lái)看一下~
十六進(jìn)制字符串編碼
我們嘗試去 Javascript Obfuscator 這個(gè)網(wǎng)站,選中 Encode Strings 復(fù)選框,將得到下面的代碼:
<pre class="prettyprint hljs markdown" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">console["x6Cx6Fx67"]("x43x6Fx6Ex61x72x64x4Cx69x20"+ 666)</pre>
它的原理很簡(jiǎn)單,就是將字符串的每個(gè) ASCII? 字符轉(zhuǎn)換為十六進(jìn)制形式(將函數(shù)調(diào)用改為用括號(hào)的形式,例如 console.log? -> console['log'] 在代碼混淆中也是相當(dāng)常見(jiàn)的做法),這就是最簡(jiǎn)單的混淆了,但是只能騙騙小白,我們可以輕易的反解:
這種技術(shù)還有一些其他變體,比如用 unicode 編碼替換字符。
https://javascriptobfuscator.com/Javascript-Obfuscator.aspx
字符串?dāng)?shù)組映射
還是在上面的網(wǎng)站,我們選中 Move Strings 這個(gè)選項(xiàng),得到的代碼是下面這樣的:
<pre class="prettyprint hljs markdown" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">var _0x8925=["x43x6Fx6Ex61x72x64x4Cx69x20","x6Cx6Fx67"];
console[_0x8925[1]](_0x8925[0]+ 666)</pre>
多了個(gè)字符串?dāng)?shù)組,通過(guò)在不同索引處引入數(shù)組來(lái)間接使用這些字符串。
死代碼注入
死代碼其實(shí)指的就是一些無(wú)法訪問(wèn)的代碼,我們可以在原本的代碼上額外注入一些永遠(yuǎn)無(wú)法訪問(wèn)的代碼來(lái)讓代碼難以閱讀,但是同時(shí)也會(huì)讓代碼變得更大。這次我們嘗試一下 defendjs:
安裝:
<pre class="hljs ruby" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">$ npm install -g https://github.com/alexhorn/defendjs.git</pre>
我們嘗試創(chuàng)建一個(gè) conardli.js 并且將上面的代碼放入這個(gè)文件,執(zhí)行下面的命令:
<pre class="hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">$ defendjs --input conardli.js --features dead_code --output .</pre>
得到了下面這一大坨代碼:
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">(function () {
function a(a, d) {
var b = new Array(0);;
var c = arguments;
while (true)
try {
switch (a) {
case 21309:
return;
case 792:
function e(a, b) {
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
}
function f() {
var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
var b = function () {
return a.Apply(this, c.concat(Array.prototype.slice.call(arguments)));
};
b.prototype = a.prototype;
return b;
}
function g(a, b) {
return Array.prototype.slice.call(a, b);
}
function h(b) {
var c = {};
for (var a = 0; a < b.length; a += 2) {
c[b[a]] = b[a + 1];
}
return c;
}
function i(a) {
return a.map(function (a) {
return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
}).join('');
}
function j() {
return String.fromCharCode.apply(null, arguments);
}
console.log('ConardLi', 666);
a = 21309;
break;
}
} catch (b) {
$defendjs$tobethrown = null;
switch (a) {
default:
throw b;
}
}
}
a(792, {});
}())</pre>
代碼很大,其實(shí)仔細(xì)分析就會(huì)發(fā)現(xiàn)其余插入的代碼都是無(wú)法運(yùn)行的:
最頂層包了一個(gè) IIFE?,然后有一個(gè) a? 函數(shù),a、b? 兩個(gè)參數(shù)。調(diào)用 a? 函數(shù)時(shí)只傳入了第一個(gè)參數(shù) 792,然后就會(huì)發(fā)現(xiàn) a 函數(shù)里有個(gè) switch? 語(yǔ)句,只會(huì)執(zhí)行到第二個(gè) case,里面是這樣的語(yǔ)句:
e、f、g、h、j、i 這幾個(gè)函數(shù)都是沒(méi)有調(diào)用的,所以只會(huì)執(zhí)行最后的 console.log('ConardLi', 666); 語(yǔ)句 ...
https://github.com/alexhorn/defendjs
作用域混淆
我們將代碼還原回去,重新執(zhí)行 defendjs? 的 scope 能力:
<pre class="hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">$ defendjs --input conardli.js --features scope --output .</pre>
<pre class="prettyprint hljs clojure" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">(function () {
{
{
function b(a, b) {
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
}
function c() {
var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
var b = function () {
return a.apply(this, c.concat(Array.prototype.slice.call(arguments)));
};
b.prototype = a.prototype;
return b;
}
function d(a, b) {
return Array.prototype.slice.call(a, b);
}
function e(b) {
var c = {};
for (var a = 0; a < b.length; a += 2) {
c[b[a]] = b[a + 1];
}
return c;
}
function f(a) {
return a.map(function (a) {
return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
}).join('');
}
function g() {
return String.fromCharCode.apply(null, arguments);
}
}
var a = [];
console.log('ConardLi', 666);
}
}())</pre>
這個(gè)可能看起來(lái)像是前面的一個(gè)簡(jiǎn)單版本,但是有一個(gè)關(guān)鍵的區(qū)別:它引入了多個(gè)具有重復(fù)標(biāo)識(shí)符的詞法作用域。例如,a? 可能是最內(nèi)層作用域中第一個(gè)函數(shù)的參數(shù),也可以是第二個(gè)函數(shù)中的變量,甚至可以是與我們的 conaole.log 語(yǔ)句相同作用域中的變量。在這個(gè)簡(jiǎn)單的示例中,很容易看穿,因?yàn)樽顑?nèi)層范圍內(nèi)的任何函數(shù)都不會(huì)在任何地方被調(diào)用,但是,現(xiàn)實(shí)的業(yè)務(wù)代碼往往是很復(fù)雜的,混淆后就不那么容易看穿了。
字符編碼
還是使用 defendjs ,對(duì)我們的代碼執(zhí)行下面的命令:
<pre class="hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">$ defendjs --input conardli.js --features literals --output .</pre>
得到下面的代碼:
<pre class="prettyprint hljs clojure" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">(function () {
function c() {
var c = arguments;
var b = [];
b[1] = '';
b[1] += a(67, 111, 110);
b[1] += a(97);
b[1] += a(114, 100);
b[1] += a(76, 105);
return b[1];
}
{
{
function e(a, b) {
return Array.prototype.slice.call(a).concat(Array.prototype.slice.call(b));
}
function d() {
var a = arguments[0], c = Array.prototype.slice.call(arguments, 1);
var b = function () {
return a.apply(this, c.concat(Array.prototype.slice.call(arguments)));
};
b.prototype = a.prototype;
return b;
}
function f(a, b) {
return Array.prototype.slice.call(a, b);
}
function g(b) {
var c = {};
for (var a = 0; a < b.length; a += 2) {
c[b[a]] = b[a + 1];
}
return c;
}
function h(a) {
return a.map(function (a) {
return String.fromCharCode(a & ~0 >>> 16) + String.fromCharCode(a >> 16);
}).join('');
}
function a() {
return String.fromCharCode.apply(null, arguments);
}
}
var b = [];
console.log(d(c, b)(), 666);
}
}())</pre>
在這種情況下,硬編碼會(huì)被轉(zhuǎn)換成 Unicode 然后重新計(jì)算,這樣直接閱讀代碼就很難再直接看穿硬編碼的字符串了。
變量縮短
Mangling 是一種為了優(yōu)化和混淆目的而縮短變量和屬性名稱的轉(zhuǎn)換。比如下面的代碼:
<pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let sixSixSix = 666;
let name = "ConardLi ";
console.log(name + sixSixSix);</pre>
我們使用 DefendJS? 的 mangling 功能:
<pre class="hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">$ defendjs --input conardli.js --features mangle --output .</pre>
得到的代碼是:
<pre class="prettyprint hljs scheme" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">(function () {
var a = 666;
var b = 'ConardLi! ';
console.log(b + a);
}())</pre>
兩個(gè)變量都被重新命名了,在這個(gè)簡(jiǎn)單的例子下還是很好分析的。但是如果是龐大的業(yè)務(wù)代碼,這會(huì)讓我們的代碼變得非常難以閱讀。
代碼壓縮
下面,綜合利用一下幾種技術(shù),執(zhí)行:
<pre class="prettyprint hljs verilog" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">defendjs --input conardli.js --output . --features=control_flow,literals,mangle,compress</pre>
得到下面的代碼:
<pre class="prettyprint hljs javascript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">(function(){function a(d,g){var b=new Array(1);;var e=arguments;while(true)t</pre>
來(lái)源:
https://developer.51cto.com/article/714813.html