日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

總結(jié)一些網(wǎng)站加密和混淆技術

 liqualife 2020-05-04

閱讀本文大概需要 10 分鐘。

我們在爬取網(wǎng)站的時候,經(jīng)常會遇到各種各樣類似加密的情形,比如說:

  • 某個網(wǎng)站的 URL 帶有一些看不太懂的長串加密參數(shù),要抓取就必須要懂得這些參數(shù)是怎么構(gòu)造的,否則我們連完整的 URL 都構(gòu)造不出來,更不用說爬取了。
  • 分析某個網(wǎng)站的 Ajax 接口的時候,可以看到接口的一些參數(shù)也是加密的,或者 Request Headers 里面也可能帶有一些加密參數(shù),如果不知道這些參數(shù)的具體構(gòu)造邏輯就沒法直接用程序來模擬這些 Ajax 請求。
  • 翻看網(wǎng)站的 JavaScript 源代碼,可以發(fā)現(xiàn)很多壓縮了或者看不太懂的字符,比如 JavaScript 文件名被編碼,JavaScript 的文件內(nèi)容都壓縮成幾行,JavaScript 變量也被修改成單個字符或者一些十六進制的字符,導致我們不好輕易根據(jù) JavaScript 找出某些接口的加密邏輯。

這些情況呢,基本上都是網(wǎng)站為了保護其本身的一些數(shù)據(jù)不被輕易抓取而采取的一些措施,我們可以把它歸類為兩大類:

  • 接口加密技術
  • JavaScript 壓縮、混淆和加密技術。

這一節(jié)課我們就來了解下這三類技術的實現(xiàn)原理。

數(shù)據(jù)保護

當今大數(shù)據(jù)時代,數(shù)據(jù)已經(jīng)變得越來越重要,網(wǎng)頁和 App 現(xiàn)在是主流的數(shù)據(jù)載體,如果其數(shù)據(jù)的接口沒有設置任何保護措施,在爬蟲工程師解決了一些基本的反爬如封 IP、驗證碼的問題之后,那么數(shù)據(jù)還是可以被輕松抓取到。

那么,有沒有可能在接口或 JavaScript 層面也加上一層防護呢?可以。

接口加密技術

網(wǎng)站運營商首先想到防護措施可能是對某些數(shù)據(jù)接口進行加密,比如說對某些 URL 的一些參數(shù)加上校驗碼或者把一些 id 信息進行編碼,使其變得難以閱讀或構(gòu)造;或者對某些接口請求加上一些 token、sign 等簽名,這樣這些請求發(fā)送到服務器時,服務器會通過客戶端發(fā)來的一些請求信息以及雙方約定好的秘鑰等來對當前的請求進行校驗,如果校驗通過,才返回對應數(shù)據(jù)結(jié)果。

比如說客戶端和服務端約定一種接口校驗邏輯,客戶端在每次請求服務端接口的時候都會附帶一個 sign 參數(shù),這個 sign 參數(shù)可能是由當前時間信息、請求的 URL、請求的數(shù)據(jù)、設備的 ID、雙方約定好的秘鑰經(jīng)過一些加密算法構(gòu)造而成的,客戶端會實現(xiàn)這個加密算法構(gòu)造 sign,然后每次請求服務器的時候附帶上這個參數(shù)。服務端會根據(jù)約定好的算法和請求的數(shù)據(jù)對 sign 進行校驗,如果校驗通過,才返回對應的數(shù)據(jù),否則拒絕響應。

JavaScript 壓縮、混淆和加密技術

接口加密技術看起來的確是一個不錯的解決方案,但單純依靠它并不能很好地解決問題。為什么呢?

對于網(wǎng)頁來說,其邏輯是依賴于 JavaScript 來實現(xiàn)的,JavaScript 有如下特點:

  • JavaScript 代碼運行于客戶端,也就是它必須要在用戶瀏覽器端加載并運行。
  • JavaScript 代碼是公開透明的,也就是說瀏覽器可以直接獲取到正在運行的 JavaScript 的源碼。

由于這兩個原因,至使 JavaScript 代碼是不安全的,任何人都可以讀、分析、復制、盜用,甚至篡改。

所以說,對于上述情形,客戶端 JavaScript 對于某些加密的實現(xiàn)是很容易被找到或模擬的,了解了加密邏輯后,模擬參數(shù)的構(gòu)造和請求也就是輕而易舉了,所以如果 JavaScript 沒有做任何層面的保護的話,接口加密技術基本上對數(shù)據(jù)起不到什么防護作用。

如果你不想讓自己的數(shù)據(jù)被輕易獲取,不想他人了解 JavaScript 邏輯的實現(xiàn),或者想降低被不懷好意的人甚至是黑客攻擊。那么你我們就需要用到 JavaScript 壓縮、混淆和加密技術了。

這里壓縮、混淆、加密技術簡述如下:

  • 代碼壓縮:即去除 JavaScript 代碼中的不必要的空格、換行等內(nèi)容,使源碼都壓縮為幾行內(nèi)容,降低代碼可讀性,當然同時也能提高網(wǎng)站的加載速度。
  • 代碼混淆:使用變量替換、字符串陣列化、控制流平坦化、多態(tài)變異、僵尸函數(shù)、調(diào)試保護等手段,使代碼變地難以閱讀和分析,達到最終保護的目的。但這不影響代碼原有功能。是理想、實用的 JavaScript 保護方案。
  • 代碼加密:可以通過某種手段將 JavaScript 代碼進行加密,轉(zhuǎn)成人無法閱讀或者解析的代碼,如將代碼完全抽象化加密,如 eval 加密。另外還有更強大的加密技術,可以直接將 JavaScript 代碼用 C/C++ 實現(xiàn),JavaScript 調(diào)用其編譯后形成的文件來執(zhí)行相應的功能,如 Emscripten 還有 WebAssembly。

下面我們對上面的技術分別予以介紹。

接口加密技術

數(shù)據(jù)一般都是通過服務器提供的接口來獲取的,網(wǎng)站或 App 可以請求某個數(shù)據(jù)接口獲取到對應的數(shù)據(jù),然后再把獲取的數(shù)據(jù)展示出來。

但有些數(shù)據(jù)是比較寶貴或私密的,這些數(shù)據(jù)肯定是需要一定層面上的保護。所以不同接口的實現(xiàn)也就對應著不同的安全防護級別,我們這里來總結(jié)下。

完全開放的接口

有些接口是沒有設置任何防護的,誰都可以調(diào)用和訪問,而且沒有任何時空限制和頻率限制。任何人只要知道了接口的調(diào)用方式就能無限制地調(diào)用。

這種接口的安全性是非常非常低的,如果接口的調(diào)用方式一旦泄露或被抓包獲取到,任何人都可以無限制地對數(shù)據(jù)進行操作或訪問。此時如果接口里面包含一些重要的數(shù)據(jù)或隱私數(shù)據(jù),就能輕易被篡改或竊取了。

接口參數(shù)加密

為了提升接口的安全性,客戶端會和服務端約定一種接口校驗方式,一般來說會使用到各種加密和編碼算法,如 Base64、Hex 編碼,MD5、AES、DES、RSA 等加密。

比如說客戶端和服務器雙方約定一個 sign 用作接口的簽名校驗,其生成邏輯是客戶端將 URL Path 進行 MD5 加密然后拼接上 URL 的某個參數(shù)再進行 Base64 編碼,最后得到一個字符串 sign,這個 sign 會通過 Request URL 的某個參數(shù)或 Request Headers 發(fā)送給服務器。服務器接收到請求后,對 URL Path 同樣進行 MD5 加密,然后拼接上 URL 的某個參數(shù),也進行 Base64 編碼也得到了一個 sign,然后比對生成的 sign 和客戶端發(fā)來的 sign 是否是一致的,如果是一致的,那就返回正確的結(jié)果,否則拒絕響應。這就是一個比較簡單的接口參數(shù)加密的實現(xiàn)。如果有人想要調(diào)用這個接口的話,必須要定義好 sign 的生成邏輯,否則是無法正常調(diào)用接口的。

以上呢就是一個基本的接口參數(shù)加密邏輯的實現(xiàn)。

當然上面的這個實現(xiàn)思路比較簡單,這里還可以增加一些時間戳信息增加時效性判斷,或增加一些非對稱加密進一步提高加密的復雜程度。但不管怎樣,只要客戶端和服務器約定好了加密和校驗邏輯,任何形式加密算法都是可以的。

這里要實現(xiàn)接口參數(shù)加密就需要用到一些加密算法,客戶端和服務器肯定也都有對應的 SDK 實現(xiàn)這些加密算法,如 JavaScript 的 crypto-js,Python 的 hashlib、Crypto 等等。

但還是如上文所說,如果是網(wǎng)頁的話,客戶端實現(xiàn)加密邏輯如果是用 JavaScript 來實現(xiàn),其源代碼對用戶是完全可見的,如果沒有對 JavaScript 做任何保護的話,是很容易弄清楚客戶端加密的流程的。

因此,我們需要對 JavaScript 利用壓縮、混淆、加密的方式來對客戶端的邏輯進行一定程度上的保護。

JavaScript 壓縮、混淆、加密

下面我們再來介紹下 JavaScript 的壓縮、混淆和加密技術。

JavaScript 壓縮

這個非常簡單,JavaScript 壓縮即去除 JavaScript 代碼中的不必要的空格、換行等內(nèi)容或者把一些可能公用的代碼進行處理實現(xiàn)共享,最后輸出的結(jié)果都壓縮為幾行內(nèi)容,代碼可讀性變得很差,同時也能提高網(wǎng)站加載速度。

如果僅僅是去除空格換行這樣的壓縮方式,其實幾乎是沒有任何防護作用的,因為這種壓縮方式僅僅是降低了代碼的直接可讀性。如果我們有一些格式化工具可以輕松將 JavaScript 代碼變得易讀,比如利用 IDE、在線工具或 Chrome 瀏覽器都能還原格式化的代碼。

目前主流的前端開發(fā)技術大多都會利用 Webpack 進行打包,Webpack 會對源代碼進行編譯和壓縮,輸出幾個打包好的 JavaScript 文件,其中我們可以看到輸出的 JavaScript 文件名帶有一些不規(guī)則字符串,同時文件內(nèi)容可能只有幾行內(nèi)容,變量名都是一些簡單字母表示。這其中就包含 JavaScript 壓縮技術,比如一些公共的庫輸出成 bundle 文件,一些調(diào)用邏輯壓縮和轉(zhuǎn)義成幾行代碼,這些都屬于 JavaScript 壓縮。另外其中也包含了一些很基礎的 JavaScript 混淆技術,比如把變量名、方法名替換成一些簡單字符,降低代碼可讀性。

但整體來說,JavaScript 壓縮技術只能在很小的程度上起到防護作用,要想真正提高防護效果還得依靠 JavaScript 混淆和加密技術。

JavaScript 混淆

JavaScript 混淆是完全是在 JavaScript 上面進行的處理,它的目的就是使得 JavaScript 變得難以閱讀和分析,大大降低代碼可讀性,是一種很實用的 JavaScript 保護方案。

JavaScript 混淆技術主要有以下幾種:

  • 變量混淆

將帶有含意的變量名、方法名、常量名隨機變?yōu)闊o意義的類亂碼字符串,降低代碼可讀性,如轉(zhuǎn)成單個字符或十六進制字符串。

  • 字符串混淆

將字符串陣列化集中放置、并可進行 MD5 或 Base64 加密存儲,使代碼中不出現(xiàn)明文字符串,這樣可以避免使用全局搜索字符串的方式定位到入口點。

  • 屬性加密

針對 JavaScript 對象的屬性進行加密轉(zhuǎn)化,隱藏代碼之間的調(diào)用關系。

  • 控制流平坦化

打亂函數(shù)原有代碼執(zhí)行流程及函數(shù)調(diào)用關系,使代碼邏變得混亂無序。

  • 僵尸代碼

隨機在代碼中插入無用的僵尸代碼、僵尸函數(shù),進一步使代碼混亂。

  • 調(diào)試保護

基于調(diào)試器特性,對當前運行環(huán)境進行檢驗,加入一些強制調(diào)試 debugger 語句,使其在調(diào)試模式下難以順利執(zhí)行 JavaScript 代碼。

  • 多態(tài)變異

使 JavaScript 代碼每次被調(diào)用時,將代碼自身即立刻自動發(fā)生變異,變化為與之前完全不同的代碼,即功能完全不變,只是代碼形式變異,以此杜絕代碼被動態(tài)分析調(diào)試。

  • 鎖定域名

使 JavaScript 代碼只能在指定域名下執(zhí)行。

  • 反格式化

如果對 JavaScript 代碼進行格式化,則無法執(zhí)行,導致瀏覽器假死。

  • 特殊編碼

將 JavaScript 完全編碼為人不可讀的代碼,如表情符號、特殊表示內(nèi)容等等。

總之,以上方案都是 JavaScript 混淆的實現(xiàn)方式,可以在不同程度上保護 JavaScript 代碼。

在前端開發(fā)中,現(xiàn)在 JavaScript 混淆主流的實現(xiàn)是 javascript-obfuscator 這個庫,利用它我們可以非常方便地實現(xiàn)頁面的混淆,它與 Webpack 結(jié)合起來,最終可以輸出壓縮和混淆后的 JavaScript 代碼,使得可讀性大大降低,難以逆向。

下面我們會介紹下 javascript-obfuscator 對代碼混淆的實現(xiàn),了解了實現(xiàn),那么自然我們就對混淆的機理有了更加深刻的認識。

javascript-obfuscator 的官網(wǎng)地址為:https:///,其官方介紹內(nèi)容如下:

?

A free and efficient obfuscator for JavaScript (including ES2017). Make your code harder to copy and prevent people from stealing your work.

它是支持 ES8 的免費、高效的 JavaScript 混淆庫,它可以使得你的 JavaScript 代碼經(jīng)過混淆后難以被復制、盜用,混淆后的代碼具有和原來的代碼一模一樣的功能。

怎么使用呢?首先,我們需要安裝好 Node.js,可以使用 npm 命令。

新建一個文件夾,比如 js-obfuscate,然后進入該文件夾,初始化工作空間:

npm init

這里會提示我們輸入一些信息,創(chuàng)建一個 package.json 文件,這就完成了項目初始化了。

接下來我們來安裝 javascript-obfuscator 這個庫:

npm install --save-dev javascript-obfuscator

接下來我們就可以編寫代碼來實現(xiàn)混淆了,如新建一個 main.js 文件,內(nèi)容如下:

const code = `
let x = '1' + 1
console.log('x', x)
`


const options = {
  compactfalse,
  controlFlowFlatteningtrue
}

const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options{
  return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))

在這里我們定義了兩個變量,一個是 code,即需要被混淆的代碼,另一個是混淆選項,是一個 Object。接下來我們引入了 javascript-obfuscator 這庫,然后定義了一個方法,傳入 code 和 options,來獲取混淆后的代碼,最后控制臺輸出混淆后的代碼。

代碼邏輯比較簡單,我們來執(zhí)行一下代碼:

node main.js

輸出結(jié)果如下:

var _0x53bf = ['log'];
(function (_0x1d84fe, _0x3aeda0{
    var _0x10a5a = function (_0x2f0a52{
        while (--_0x2f0a52) {
            _0x1d84fe['push'](_0x1d84fe['shift']());
        }
    };
    _0x10a5a(++_0x3aeda0);
}(_0x53bf, 0x172));
var _0x480a = function (_0x4341e5, _0x5923b4{
    _0x4341e5 = _0x4341e5 - 0x0;
    var _0xb3622e = _0x53bf[_0x4341e5];
    return _0xb3622e;
};
let x = '1' + 0x1;
console[_0x480a('0x0')]('x', x);

看到了吧,那么簡單的兩行代碼,被我們混淆成了這個樣子,其實這里我們就是設定了一個「控制流扁平化」的選項。

整體看來,代碼的可讀性大大降低,也大大加大了 JavaScript 調(diào)試的難度。

好,那么我們來跟著 javascript-obfuscator 走一遍,就能具體知道 JavaScript 混淆到底有多少方法了。

代碼壓縮

這里 javascript-obfuscator 也提供了代碼壓縮的功能,使用其參數(shù) compact 即可完成 JavaScript 代碼的壓縮,輸出為一行內(nèi)容。默認是 true,如果定義為 false,則混淆后的代碼會分行顯示。

示例如下:

const code = `
let x = '1' + 1
console.log('x', x)
`

const options = {
  compactfalse
}

這里我們先把代碼壓縮 compact 選項設置為 false,運行結(jié)果如下:

let x = '1' + 0x1;
console['log']('x', x);

如果不設置 compact 或把 compact 設置為 true,結(jié)果如下:

var _0x151c=['log'];(function(_0x1ce384,_0x20a7c7){var _0x25fc92=function(_0x188aec){while(--_0x188aec){_0x1ce384['push'](_0x1ce384['shift']());}};_0x25fc92(++_0x20a7c7);}(_0x151c,0x1b7));var _0x553e=function(_0x259219,_0x241445){_0x259219=_0x259219-0x0;var _0x56d72d=_0x151c[_0x259219];return _0x56d72d;};let x='1'+0x1;console[_0x553e('0x0')]('x',x);

可以看到單行顯示的時候,對變量名進行了進一步的混淆和控制流扁平化操作。

變量名混淆

變量名混淆可以通過配置 identifierNamesGenerator 參數(shù)實現(xiàn),我們通過這個參數(shù)可以控制變量名混淆的方式,如 hexadecimal 則會替換為 16 進制形式的字符串,在這里我們可以設定如下值:

  • hexadecimal:將變量名替換為 16 進制形式的字符串,如 0xabc123
  • mangled:將變量名替換為普通的簡寫字符,如 a、bc 等。

該參數(shù)默認為 hexadecimal。

我們將該參數(shù)修改為 mangled 來試一下:

const code = `
let hello = '1' + 1
console.log('hello', hello)
`

const options = {
  compacttrue,
  identifierNamesGenerator'mangled'
}

運行結(jié)果如下:

var a=['hello'];(function(c,d){var e=function(f){while(--f){c['push'](c['shift']());}};e(++d);}(a,0x9b));var b=function(c,d){c=c-0x0;var e=a[c];return e;};let hello='1'+0x1;console['log'](b('0x0'),hello);

可以看到這里的變量命名都變成了 a、b 等形式。

如果我們將 identifierNamesGenerator 修改為 hexadecimal 或者不設置,運行結(jié)果如下:

var _0x4e98=['log','hello'];(function(_0x4464de,_0x39de6c){var _0xdffdda=function(_0x6a95d5){while(--_0x6a95d5){_0x4464de['push'](_0x4464de['shift']());}};_0xdffdda(++_0x39de6c);}(_0x4e98,0xc8));var _0x53cb=function(_0x393bda,_0x8504e7){_0x393bda=_0x393bda-0x0;var _0x46ab80=_0x4e98[_0x393bda];return _0x46ab80;};let hello='1'+0x1;console[_0x53cb('0x0')](_0x53cb('0x1'),hello);

可以看到選用了 mangled,其代碼體積會更小,但 hexadecimal 其可讀性會更低。

另外我們還可以通過設置 identifiersPrefix 參數(shù)來控制混淆后的變量前綴,示例如下:

const code = `
let hello = '1' + 1
console.log('hello', hello)
`

const options = {
  identifiersPrefix'germey'
}

運行結(jié)果:

var germey_0x3dea=['log','hello'];(function(_0x348ff3,_0x5330e8){var _0x1568b1=function(_0x4740d8){while(--_0x4740d8){_0x348ff3['push'](_0x348ff3['shift']());}};_0x1568b1(++_0x5330e8);}(germey_0x3dea,0x94));var germey_0x30e4=function(_0x2e8f7c,_0x1066a8){_0x2e8f7c=_0x2e8f7c-0x0;var _0x5166ba=germey_0x3dea[_0x2e8f7c];return _0x5166ba;};let hello='1'+0x1;console[germey_0x30e4('0x0')](germey_0x30e4('0x1'),hello);

可以看到混淆后的變量前綴加上了我們自定義的字符串 germey。

另外 renameGlobals 這個參數(shù)還可以指定是否混淆全局變量和函數(shù)名稱,默認為 false。示例如下:

const code = `
var $ = function(id) {
    return document.getElementById(id);
};
`

const options = {
  renameGlobalstrue
}

運行結(jié)果如下:

var _0x4864b0=function(_0x5763be){return document['getElementById'](_0x5763be);};

可以看到這里我們聲明了一個全局變量 $,在 renameGlobals 設置為 true 之后,$ 這個變量也被替換了。如果后文用到了這個 $ 對象,可能就會有找不到定義的錯誤,因此這個參數(shù)可能導致代碼執(zhí)行不通。

如果我們不設置 renameGlobals 或者設置為 false,結(jié)果如下:

var _0x239a=['getElementById'];(function(_0x3f45a3,_0x583dfa){var _0x2cade2=function(_0x28479a){while(--_0x28479a){_0x3f45a3['push'](_0x3f45a3['shift']());}};_0x2cade2(++_0x583dfa);}(_0x239a,0xe1));var _0x3758=function(_0x18659d,_0x50c21d){_0x18659d=_0x18659d-0x0;var _0x531b8d=_0x239a[_0x18659d];return _0x531b8d;};var $=function(_0x3d8723){return document[_0x3758('0x0')](_0x3d8723);};

可以看到,最后還是有 $ 的聲明,其全局名稱沒有被改變。

字符串混淆

字符串混淆,即將一個字符串聲明放到一個數(shù)組里面,使之無法被直接搜索到。我們可以通過控制 stringArray 參數(shù)來控制,默認為 true。

我們還可以通過 rotateStringArray 參數(shù)來控制數(shù)組化后結(jié)果的的元素順序,默認為 true。

還可以通過 stringArrayEncoding 參數(shù)來控制數(shù)組的編碼形式,默認不開啟編碼,如果設置為 true 或 base64,則會使用 Base64 編碼,如果設置為 rc4,則使用 RC4 編碼。

還可以通過 stringArrayThreshold 來控制啟用編碼的概率,范圍 0 到 1,默認 0.8。

示例如下:

const code = `
var a = 'hello world'   
`

const options = {
  stringArraytrue,
  rotateStringArraytrue,
  stringArrayEncodingtrue// 'base64' or 'rc4' or false
  stringArrayThreshold: 1,
}

運行結(jié)果如下:

var _0x4215=['aGVsbG8gd29ybGQ='];(function(_0x42bf17,_0x4c348f){var _0x328832=function(_0x355be1){while(--_0x355be1){_0x42bf17['push'](_0x42bf17['shift']());}};_0x328832(++_0x4c348f);}(_0x4215,0x1da));var _0x5191=function(_0x3cf2ba,_0x1917d8){_0x3cf2ba=_0x3cf2ba-0x0;var _0x1f93f0=_0x4215[_0x3cf2ba];if(_0x5191['LqbVDH']===undefined){(function(){var _0x5096b2;try{var _0x282db1=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x5096b2=_0x282db1();}catch(_0x2acb9c){_0x5096b2=window;}var _0x388c14='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x5096b2['atob']||(_0x5096b2['atob']=function(_0x4cc27c){var _0x2af4ae=String(_0x4cc27c)['replace'](/=+$/,'');for(var _0x21400b=0x0,_0x3f4e2e,_0x5b193b,_0x233381=0x0,_0x3dccf7='';_0x5b193b=_0x2af4ae['charAt'](_0x233381++);~_0x5b193b&&(_0x3f4e2e=_0x21400b%0x4?_0x3f4e2e*0x40+_0x5b193b:_0x5b193b,_0x21400b++%0x4)?_0x3dccf7+=String['fromCharCode'](0xff&_0x3f4e2e>>(-0x2*_0x21400b&0x6)):0x0){_0x5b193b=_0x388c14['indexOf'](_0x5b193b);}return _0x3dccf7;});}());_0x5191['DuIurT']=function(_0x51888e){var _0x29801f=atob(_0x51888e);var _0x561e62=[];for(var _0x5dd788=0x0,_0x1a8b73=_0x29801f['length'];_0x5dd788<_0x1a8b73;_0x5dd788++){_0x561e62+='%'+('00'+_0x29801f['charCodeAt'](_0x5dd788)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x561e62);};_0x5191['mgoBRd']={};_0x5191['LqbVDH']=!![];}var _0x1741f0=_0x5191['mgoBRd'][_0x3cf2ba];if(_0x1741f0===undefined){_0x1f93f0=_0x5191['DuIurT'](_0x1f93f0);_0x5191['mgoBRd'][_0x3cf2ba]=_0x1f93f0;}else{_0x1f93f0=_0x1741f0;}return _0x1f93f0;};var a=_0x5191('0x0');

可以看到這里就把字符串進行了 Base64 編碼,我們再也無法通過查找的方式找到字符串的位置了。

如果將 stringArray 設置為 false 的話,輸出就是這樣:

var a='hello\x20world';

字符串就仍然是明文顯示的,沒有被編碼。

另外我們還可以使用 unicodeEscapeSequence 這個參數(shù)對字符串進行 Unicode 轉(zhuǎn)碼,使之更加難以辨認,示例如下:

const code = `
var a = 'hello world'
`

const options = {
  compactfalse,
  unicodeEscapeSequencetrue
}

運行結(jié)果如下:

var _0x5c0d = ['\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64'];
(function (_0x54cc9c, _0x57a3b2{
    var _0xf833cf = function (_0x3cd8c6{
        while (--_0x3cd8c6) {
            _0x54cc9c['push'](_0x54cc9c['shift']());
        }
    };
    _0xf833cf(++_0x57a3b2);
}(_0x5c0d, 0x17d));
var _0x28e8 = function (_0x3fd645, _0x2cf5e7{
    _0x3fd645 = _0x3fd645 - 0x0;
    var _0x298a20 = _0x5c0d[_0x3fd645];
    return _0x298a20;
};
var a = _0x28e8('0x0');

可以看到,這里字符串被數(shù)字化和 Unicode 化,非常難以辨認。

在很多 JavaScript 逆向的過程中,一些關鍵的字符串可能會作為切入點來查找加密入口。用了這種混淆之后,如果有人想通過全局搜索的方式搜索 hello 這樣的字符串找加密入口,也沒法搜到了。

代碼自我保護

我們可以通過設置 selfDefending 參數(shù)來開啟代碼自我保護功能。開啟之后,混淆后的 JavaScript 會以強制一行形式顯示,如果我們將混淆后的代碼進行格式化(美化)或者重命名,該段代碼將無法執(zhí)行。

例如:

const code = `
console.log('hello world')
`

const options = {
  selfDefendingtrue
}

運行結(jié)果如下:

var _0x26da=['log','hello\x20world'];(function(_0x190327,_0x57c2c0){var _0x577762=function(_0xc9dabb){while(--_0xc9dabb){_0x190327['push'](_0x190327['shift']());}};var _0x35976e=function(){var _0x16b3fe={'data':{'key':'cookie','value':'timeout'},'setCookie':function(_0x2d52d5,_0x16feda,_0x57cadf,_0x56056f){_0x56056f=_0x56056f||{};var _0x5b6dc3=_0x16feda+'='+_0x57cadf;var _0x333ced=0x0;for(var _0x333ced=0x0,_0x19ae36=_0x2d52d5['length'];_0x333ced<_0x19ae36;_0x333ced++){var _0x409587=_0x2d52d5[_0x333ced];_0x5b6dc3+=';\x20'+_0x409587;var _0x4aa006=_0x2d52d5[_0x409587];_0x2d52d5['push'](_0x4aa006);_0x19ae36=_0x2d52d5['length'];if(_0x4aa006!==!![]){_0x5b6dc3+='='+_0x4aa006;}}_0x56056f['cookie']=_0x5b6dc3;},'removeCookie':function(){return'dev';},'getCookie':function(_0x30c497,_0x51923d){_0x30c497=_0x30c497||function(_0x4b7e18){return _0x4b7e18;};var _0x557e06=_0x30c497(new RegExp('(?:^|;\x20)'+_0x51923d['replace'](/([.$?*|{}()[]\/+^])/g,'$1')+'=([^;]*)'));var _0x817646=function(_0xf3fae7,_0x5d8208){_0xf3fae7(++_0x5d8208);};_0x817646(_0x577762,_0x57c2c0);return _0x557e06?decodeURIComponent(_0x557e06[0x1]):undefined;}};var _0x4673cd=function(){var _0x4c6c5c=new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');return _0x4c6c5c['test'](_0x16b3fe['removeCookie']['toString']());};_0x16b3fe['updateCookie']=_0x4673cd;var _0x5baa80='';var _0x1faf19=_0x16b3fe['updateCookie']();if(!_0x1faf19){_0x16b3fe['setCookie'](['*'],'counter',0x1);}else if(_0x1faf19){_0x5baa80=_0x16b3fe['getCookie'](null,'counter');}else{_0x16b3fe['removeCookie']();}};_0x35976e();}(_0x26da,0x140));var _0x4391=function(_0x1b42d8,_0x57edc8){_0x1b42d8=_0x1b42d8-0x0;var _0x2fbeca=_0x26da[_0x1b42d8];return _0x2fbeca;};var _0x197926=function(){var _0x10598f=!![];return function(_0xffa3b3,_0x7a40f9){var _0x48e571=_0x10598f?function(){if(_0x7a40f9){var _0x2194b5=_0x7a40f9['apply'](_0xffa3b3,arguments);_0x7a40f9=null;return _0x2194b5;}}:function(){};_0x10598f=![];return _0x48e571;};}();var _0x2c6fd7=_0x197926(this,function(){var _0x4828bb=function(){return'\x64\x65\x76';},_0x35c3bc=function(){return'\x77\x69\x6e\x64\x6f\x77';};var _0x456070=function(){var _0x4576a4=new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');return!_0x4576a4['\x74\x65\x73\x74'](_0x4828bb['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x3fde69=function(){var _0xabb6f4=new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');return _0xabb6f4['\x74\x65\x73\x74'](_0x35c3bc['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x2d9a50=function(_0x58fdb4){var _0x2a6361=~-0x1>>0x1+0xff%0x0;if(_0x58fdb4['\x69\x6e\x64\x65\x78\x4f\x66']('\x69'===_0x2a6361)){_0xc388c5(_0x58fdb4);}};var _0xc388c5=function(_0x2073d6){var _0x6bb49f=~-0x4>>0x1+0xff%0x0;if(_0x2073d6['\x69\x6e\x64\x65\x78\x4f\x66']((!![]+'')[0x3])!==_0x6bb49f){_0x2d9a50(_0x2073d6);}};if(!_0x456070()){if(!_0x3fde69()){_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');}else{_0x2d9a50('\x69\x6e\x64\x65\x78\x4f\x66');}}else{_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');}});_0x2c6fd7();console[_0x4391('0x0')](_0x4391('0x1'));

如果我們將上述代碼放到控制臺,它的執(zhí)行結(jié)果和之前是一模一樣的,沒有任何問題。

如果我們將其進行格式化,會變成如下內(nèi)容:

var _0x26da = ['log''hello\x20world'];
(function (_0x190327, _0x57c2c0{
    var _0x577762 = function (_0xc9dabb{
        while (--_0xc9dabb) {
            _0x190327['push'](_0x190327['shift']());
        }
    };
    var _0x35976e = function () {
        var _0x16b3fe = {
            'data': {
                'key''cookie',
                'value''timeout'
            },
            'setCookie'function (_0x2d52d5, _0x16feda, _0x57cadf, _0x56056f{
                _0x56056f = _0x56056f || {};
                var _0x5b6dc3 = _0x16feda + '=' + _0x57cadf;
                var _0x333ced = 0x0;
                for (var _0x333ced = 0x0, _0x19ae36 = _0x2d52d5['length']; _0x333ced < _0x19ae36; _0x333ced++) {
                    var _0x409587 = _0x2d52d5[_0x333ced];
                    _0x5b6dc3 += ';\x20' + _0x409587;
                    var _0x4aa006 = _0x2d52d5[_0x409587];
                    _0x2d52d5['push'](_0x4aa006);
                    _0x19ae36 = _0x2d52d5['length'];
                    if (_0x4aa006 !== !![]) {
                        _0x5b6dc3 += '=' + _0x4aa006;
                    }
                }
                _0x56056f['cookie'] = _0x5b6dc3;
            }, 'removeCookie'function () {
                return 'dev';
            }, 'getCookie'function (_0x30c497, _0x51923d{
                _0x30c497 = _0x30c497 || function (_0x4b7e18{
                    return _0x4b7e18;
                };
                var _0x557e06 = _0x30c497(new RegExp('(?:^|;\x20)' + _0x51923d['replace'](/([.$?*|{}()[]\/+^])/g'$1') + '=([^;]*)'));
                var _0x817646 = function (_0xf3fae7, _0x5d8208{
                    _0xf3fae7(++_0x5d8208);
                };
                _0x817646(_0x577762, _0x57c2c0);
                return _0x557e06 ? decodeURIComponent(_0x557e06[0x1]) : undefined;
            }
        };
        var _0x4673cd = function () {
            var _0x4c6c5c = new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');
            return _0x4c6c5c['test'](_0x16b3fe['removeCookie']['toString']());
        };
        _0x16b3fe['updateCookie'] = _0x4673cd;
        var _0x5baa80 = '';
        var _0x1faf19 = _0x16b3fe['updateCookie']();
        if (!_0x1faf19) {
            _0x16b3fe['setCookie'](['*'], 'counter'0x1);
        } else if (_0x1faf19) {
            _0x5baa80 = _0x16b3fe['getCookie'](null'counter');
        } else {
            _0x16b3fe['removeCookie']();
        }
    };
    _0x35976e();
}(_0x26da, 0x140));
var _0x4391 = function (_0x1b42d8, _0x57edc8{
    _0x1b42d8 = _0x1b42d8 - 0x0;
    var _0x2fbeca = _0x26da[_0x1b42d8];
    return _0x2fbeca;
};
var _0x197926 = function () {
    var _0x10598f = !![];
    return function (_0xffa3b3, _0x7a40f9{
        var _0x48e571 = _0x10598f ? function () {
            if (_0x7a40f9) {
                var _0x2194b5 = _0x7a40f9['apply'](_0xffa3b3, arguments);
                _0x7a40f9 = null;
                return _0x2194b5;
            }
        } : function () {};
        _0x10598f = ![];
        return _0x48e571;
    };
}();
var _0x2c6fd7 = _0x197926(thisfunction () {
    var _0x4828bb = function () {
            return '\x64\x65\x76';
        },
        _0x35c3bc = function () {
            return '\x77\x69\x6e\x64\x6f\x77';
        };
    var _0x456070 = function () {
        var _0x4576a4 = new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');
        return !_0x4576a4['\x74\x65\x73\x74'](_0x4828bb['\x74\x6f\x53\x74\x72\x69\x6e\x67']());
    };
    var _0x3fde69 = function () {
        var _0xabb6f4 = new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');
        return _0xabb6f4['\x74\x65\x73\x74'](_0x35c3bc['\x74\x6f\x53\x74\x72\x69\x6e\x67']());
    };
    var _0x2d9a50 = function (_0x58fdb4{
        var _0x2a6361 = ~-0x1 >> 0x1 + 0xff % 0x0;
        if (_0x58fdb4['\x69\x6e\x64\x65\x78\x4f\x66']('\x69' === _0x2a6361)) {
            _0xc388c5(_0x58fdb4);
        }
    };
    var _0xc388c5 = function (_0x2073d6{
        var _0x6bb49f = ~-0x4 >> 0x1 + 0xff % 0x0;
        if (_0x2073d6['\x69\x6e\x64\x65\x78\x4f\x66']((!![] + '')[0x3]) !== _0x6bb49f) {
            _0x2d9a50(_0x2073d6);
        }
    };
    if (!_0x456070()) {
        if (!_0x3fde69()) {
            _0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');
        } else {
            _0x2d9a50('\x69\x6e\x64\x65\x78\x4f\x66');
        }
    } else {
        _0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');
    }
});
_0x2c6fd7();
console[_0x4391('0x0')](_0x4391('0x1'));

如果吧這段代碼放到瀏覽器里面,瀏覽器會直接卡死無法運行。這樣如果有人對代碼進行了格式化,就無法正常對代碼進行運行和調(diào)試,從而起到了保護作用。

控制流平坦化

控制流平坦化其實就是將代碼的執(zhí)行邏輯混淆,使其變得復雜難讀。其基本思想是將一些邏輯處理塊都統(tǒng)一加上一個前驅(qū)邏輯塊,每個邏輯塊都由前驅(qū)邏輯塊進行條件判斷和分發(fā),構(gòu)成一個個閉環(huán)邏輯,導致整個執(zhí)行邏輯十分復雜難讀。

我們通過 controlFlowFlattening 變量可以控制是否開啟控制流平坦化,示例如下:

const code = `
(function(){
    function foo () {
        return function () {
            var sum = 1 + 2;
            console.log(1);
            console.log(2);
            console.log(3);
            console.log(4);
            console.log(5);
            console.log(6);
        }
    }
    
    foo()();
})();
`

const options = {
  compactfalse,
  controlFlowFlatteningtrue
}

輸出結(jié)果如下:

var _0xbaf1 = [
    'dZwUe',
    'log',
    'fXqMu',
    '0|1|3|4|6|5|2',
    'chYMl',
    'IZEsA',
    'split'
];
(function (_0x22d342, _0x4f6332{
    var _0x43ff59 = function (_0x5ad417{
        while (--_0x5ad417) {
            _0x22d342['push'](_0x22d342['shift']());
        }
    };
    _0x43ff59(++_0x4f6332);
}(_0xbaf1, 0x192));
var _0x1a69 = function (_0x8d64b1, _0x5e07b3{
    _0x8d64b1 = _0x8d64b1 - 0x0;
    var _0x300bab = _0xbaf1[_0x8d64b1];
    return _0x300bab;
};
(function () {
    var _0x19d8ce = {
        'chYMl': _0x1a69('0x0'),
        'IZEsA'function (_0x22e521, _0x298a22{
            return _0x22e521 + _0x298a22;
        },
        'fXqMu'function (_0x13124b{
            return _0x13124b();
        }
    };
    function _0x4e2ee0() {
        var _0x118a6a = {
            'LZAQV': _0x19d8ce[_0x1a69('0x1')],
            'dZwUe'function (_0x362ef3, _0x352709{
                return _0x19d8ce[_0x1a69('0x2')](_0x362ef3, _0x352709);
            }
        };
        return function () {
            var _0x4c336d = _0x118a6a['LZAQV'][_0x1a69('0x3')]('|'), _0x2b6466 = 0x0;
            while (!![]) {
                switch (_0x4c336d[_0x2b6466++]) {
                case '0':
                    var _0xbfa3fd = _0x118a6a[_0x1a69('0x4')](0x10x2);
                    continue;
                case '1':
                    console['log'](0x1);
                    continue;
                case '2':
                    console[_0x1a69('0x5')](0x6);
                    continue;
                case '3':
                    console[_0x1a69('0x5')](0x2);
                    continue;
                case '4':
                    console[_0x1a69('0x5')](0x3);
                    continue;
                case '5':
                    console[_0x1a69('0x5')](0x5);
                    continue;
                case '6':
                    console[_0x1a69('0x5')](0x4);
                    continue;
                }
                break;
            }
        };
    }
    _0x19d8ce[_0x1a69('0x6')](_0x4e2ee0)();
}());

可以看到,一些連續(xù)的執(zhí)行邏輯被打破,代碼被修改為一個 switch 語句,我們很難再一眼看出多條 console.log 語句的執(zhí)行順序了。

如果我們將 controlFlowFlattening 設置為 false 或者不設置,運行結(jié)果如下:

var _0x552c = ['log'];
(function (_0x4c4fa0, _0x59faa0{
    var _0xa01786 = function (_0x409a37{
        while (--_0x409a37) {
            _0x4c4fa0['push'](_0x4c4fa0['shift']());
        }
    };
    _0xa01786(++_0x59faa0);
}(_0x552c, 0x9b));
var _0x4e63 = function (_0x75ea1a, _0x50e176{
    _0x75ea1a = _0x75ea1a - 0x0;
    var _0x59dc94 = _0x552c[_0x75ea1a];
    return _0x59dc94;
};
(function () {
    function _0x507f38() {
        return function () {
            var _0x17fb7e = 0x1 + 0x2;
            console[_0x4e63('0x0')](0x1);
            console['log'](0x2);
            console['log'](0x3);
            console[_0x4e63('0x0')](0x4);
            console[_0x4e63('0x0')](0x5);
            console[_0x4e63('0x0')](0x6);
        };
    }
    _0x507f38()();
}());

可以看到,這里仍然保留了原始的 console.log 執(zhí)行邏輯。

因此,使用控制流扁平化可以使得執(zhí)行邏輯更加復雜難讀,目前非常多的前端混淆都會加上這個選項。

但啟用控制流扁平化之后,代碼的執(zhí)行時間會變長,最長達 1.5 倍之多。

另外我們還能使用 controlFlowFlatteningThreshold 這個參數(shù)來控制比例,取值范圍是 0 到 1,默認 0.75,如果設置為 0,那相當于 controlFlowFlattening 設置為 false,即不開啟控制流扁平化 。

僵尸代碼注入

僵尸代碼即不會被執(zhí)行的代碼或?qū)ι舷挛臎]有任何影響的代碼,注入之后可以對現(xiàn)有的 JavaScript 代碼的閱讀形成干擾。我們可以使用 deadCodeInjection 參數(shù)開啟這個選項,默認為 false。

示例如下:

const code = `
(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
            console.log('cde');
            console.log('efg');
            console.log('hij');
        };
        
        var bar = function () {
            console.log('klm');
            console.log('nop');
            console.log('qrs');
        };
    
        var baz = function () {
            console.log('tuv');
            console.log('wxy');
            console.log('z');
        };
    
        foo();
        bar();
        baz();
    }
})();
`

const options = {
  compactfalse,
  deadCodeInjectiontrue
}

運行結(jié)果如下:

var _0x5024 = [
    'zaU',
    'log',
    'tuv',
    'wxy',
    'abc',
    'cde',
    'efg',
    'hij',
    'QhG',
    'TeI',
    'klm',
    'nop',
    'qrs',
    'bZd',
    'HMx'
];
var _0x4502 = function (_0x1254b1, _0x583689{
    _0x1254b1 = _0x1254b1 - 0x0;
    var _0x529b49 = _0x5024[_0x1254b1];
    return _0x529b49;
};
(function () {
    if (!![]) {
        var _0x16c18d = function () {
            if (_0x4502('0x0') !== _0x4502('0x0')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        var _0x1f7292 = function () {
            if (_0x4502('0x8') === _0x4502('0x9')) {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            } else {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            }
        };
        var _0x33b212 = function () {
            if (_0x4502('0xd') !== _0x4502('0xe')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        _0x16c18d();
        _0x1f7292();
        _0x33b212();
    }
}());

可見這里增加了一些不會執(zhí)行到的邏輯區(qū)塊內(nèi)容。

如果將 deadCodeInjection 設置為 false 或者不設置,運行結(jié)果如下:

var _0x402a = [
    'qrs',
    'wxy',
    'log',
    'abc',
    'cde',
    'efg',
    'hij',
    'nop'
];
(function (_0x57239e, _0x4747e8{
    var _0x3998cd = function (_0x34a502{
        while (--_0x34a502) {
            _0x57239e['push'](_0x57239e['shift']());
        }
    };
    _0x3998cd(++_0x4747e8);
}(_0x402a, 0x162));
var _0x5356 = function (_0x2f2c10, _0x2878a6{
    _0x2f2c10 = _0x2f2c10 - 0x0;
    var _0x4cfe02 = _0x402a[_0x2f2c10];
    return _0x4cfe02;
};
(function () {
    if (!![]) {
        var _0x60edc1 = function () {
            console[_0x5356('0x0')](_0x5356('0x1'));
            console[_0x5356('0x0')](_0x5356('0x2'));
            console[_0x5356('0x0')](_0x5356('0x3'));
            console['log'](_0x5356('0x4'));
        };
        var _0x56405f = function () {
            console[_0x5356('0x0')]('klm');
            console['log'](_0x5356('0x5'));
            console['log'](_0x5356('0x6'));
        };
        var _0x332d12 = function () {
            console[_0x5356('0x0')]('tuv');
            console[_0x5356('0x0')](_0x5356('0x7'));
            console['log']('z');
        };
        _0x60edc1();
        _0x56405f();
        _0x332d12();
    }
}());

另外我們還可以通過設置 deadCodeInjectionThreshold 參數(shù)來控制僵尸代碼注入的比例,取值 0 到 1,默認是 0.4。

僵尸代碼可以起到一定的干擾作用,所以在有必要的時候也可以注入。

對象鍵名替換

如果是一個對象,可以使用 transformObjectKeys 來對對象的鍵值進行替換,示例如下:

const code = `
(function(){
    var object = {
        foo: 'test1',
        bar: {
            baz: 'test2'
        }
    };
})(); 
`

const options = {
  compactfalse,
  transformObjectKeystrue
}

輸出結(jié)果如下:

var _0x7a5d = [
    'bar',
    'test2',
    'test1'
];
(function (_0x59fec5, _0x2e4fac{
    var _0x231e7a = function (_0x46f33e{
        while (--_0x46f33e) {
            _0x59fec5['push'](_0x59fec5['shift']());
        }
    };
    _0x231e7a(++_0x2e4fac);
}(_0x7a5d, 0x167));
var _0x3bc4 = function (_0x309ad3, _0x22d5ac{
    _0x309ad3 = _0x309ad3 - 0x0;
    var _0x3a034e = _0x7a5d[_0x309ad3];
    return _0x3a034e;
};
(function () {
    var _0x9f1fd1 = {};
    _0x9f1fd1['foo'] = _0x3bc4('0x0');
    _0x9f1fd1[_0x3bc4('0x1')] = {};
    _0x9f1fd1[_0x3bc4('0x1')]['baz'] = _0x3bc4('0x2');
}());

可以看到,Object 的變量名被替換為了特殊的變量,這也可以起到一定的防護作用。

禁用控制臺輸出

可以使用 disableConsoleOutput 來禁用掉 console.log 輸出功能,加大調(diào)試難度,示例如下:

const code = `
console.log('hello world')
`

const options = {
  disableConsoleOutputtrue
}

運行結(jié)果如下:

var _0x3a39=['debug','info','error','exception','trace','hello\x20world','apply','{}.constructor(\x22return\x20this\x22)(\x20)','console','log','warn'];(function(_0x2a157a,_0x5d9d3b){var _0x488e2c=function(_0x5bcb73){while(--_0x5bcb73){_0x2a157a['push'](_0x2a157a['shift']());}};_0x488e2c(++_0x5d9d3b);}(_0x3a39,0x10e));var _0x5bff=function(_0x43bdfc,_0x52e4c6){_0x43bdfc=_0x43bdfc-0x0;var _0xb67384=_0x3a39[_0x43bdfc];return _0xb67384;};var _0x349b01=function(){var _0x1f484b=!![];return function(_0x5efe0d,_0x33db62){var _0x20bcd2=_0x1f484b?function(){if(_0x33db62){var _0x77054c=_0x33db62[_0x5bff('0x0')](_0x5efe0d,arguments);_0x33db62=null;return _0x77054c;}}:function(){};_0x1f484b=![];return _0x20bcd2;};}();var _0x19f538=_0x349b01(this,function(){var _0x7ab6e4=function(){};var _0x157bff;try{var _0x5e672c=Function('return\x20(function()\x20'+_0x5bff('0x1')+');');_0x157bff=_0x5e672c();}catch(_0x11028d){_0x157bff=window;}if(!_0x157bff[_0x5bff('0x2')]){_0x157bff[_0x5bff('0x2')]=function(_0x7ab6e4){var _0x5a8d9e={};_0x5a8d9e[_0x5bff('0x3')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x4')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x5')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x6')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x7')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x8')]=_0x7ab6e4;_0x5a8d9e[_0x5bff('0x9')]=_0x7ab6e4;return _0x5a8d9e;}(_0x7ab6e4);}else{_0x157bff[_0x5bff('0x2')][_0x5bff('0x3')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x4')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')]['debug']=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x6')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x7')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x8')]=_0x7ab6e4;_0x157bff[_0x5bff('0x2')][_0x5bff('0x9')]=_0x7ab6e4;}});_0x19f538();console[_0x5bff('0x3')](_0x5bff('0xa'));

此時,我們?nèi)绻麍?zhí)行這個代碼,發(fā)現(xiàn)是沒有任何輸出的,這里實際上就是將 console 的一些功能禁用了,加大了調(diào)試難度。

調(diào)試保護

我們可以使用 debugProtection 來禁用調(diào)試模式,進入無限 Debug 模式。另外我們還可以使用 debugProtectionInterval 來啟用無限 Debug 的間隔,使得代碼在調(diào)試過程中會不斷進入斷點模式,無法順暢執(zhí)行。

示例如下:

const code = `
for (let i = 0; i < 5; i ++) {
  console.log('i', i)
}
`

const options = {
  debugProtectiontrue
}

運行結(jié)果如下:

var _0x41d0=['action','debu','stateObject','function\x20*\x5c(\x20*\x5c)','\x5c+\x5c+\x20*(?:_0x(?:[a-f0-9]){4,6}|(?:\x5cb|\x5cd)[a-z0-9]{1,4}(?:\x5cb|\x5cd))','init','test','chain','input','log','string','constructor','while\x20(true)\x20{}','apply','gger','call'];(function(_0x69147e,_0x180e03){var _0x2cc589=function(_0x18d18c){while(--_0x18d18c){_0x69147e['push'](_0x69147e['shift']());}};_0x2cc589(++_0x180e03);}(_0x41d0,0x153));var _0x16d2=function(_0x3d813e,_0x59f7b2){_0x3d813e=_0x3d813e-0x0;var _0x228f98=_0x41d0[_0x3d813e];return _0x228f98;};var _0x241eee=function(){var _0xeb17=!![];return function(_0x5caffe,_0x2bb267){var _0x16e1bf=_0xeb17?function(){if(_0x2bb267){var _0x573619=_0x2bb267['apply'](_0x5caffe,arguments);_0x2bb267=null;return _0x573619;}}:function(){};_0xeb17=![];return _0x16e1bf;};}();(function(){_0x241eee(this,function(){var _0x5de4a4=new RegExp(_0x16d2('0x0'));var _0x4a170e=new RegExp(_0x16d2('0x1'),'i');var _0x5351d7=_0x227210(_0x16d2('0x2'));if(!_0x5de4a4[_0x16d2('0x3')](_0x5351d7+_0x16d2('0x4'))||!_0x4a170e[_0x16d2('0x3')](_0x5351d7+_0x16d2('0x5'))){_0x5351d7('0');}else{_0x227210();}})();}());for(let i=0x0;i<0x5;i++){console[_0x16d2('0x6')]('i',i);}function _0x227210(_0x30bc32){function _0x1971c7(_0x19628c){if(typeof _0x19628c===_0x16d2('0x7')){return function(_0x3718f7){}[_0x16d2('0x8')](_0x16d2('0x9'))[_0x16d2('0xa')]('counter');}else{if((''+_0x19628c/_0x19628c)['length']!==0x1||_0x19628c%0x14===0x0){(function(){return!![];}[_0x16d2('0x8')]('debu'+_0x16d2('0xb'))[_0x16d2('0xc')](_0x16d2('0xd')));}else{(function(){return![];}[_0x16d2('0x8')](_0x16d2('0xe')+_0x16d2('0xb'))[_0x16d2('0xa')](_0x16d2('0xf')));}}_0x1971c7(++_0x19628c);}try{if(_0x30bc32){return _0x1971c7;}else{_0x1971c7(0x0);}}catch(_0x58d434){}}

如果我們將代碼粘貼到控制臺,其會不斷跳到 debugger 代碼的位置,無法順暢執(zhí)行。

域名鎖定

我們可以通過控制 domainLock 來控制 JavaScript 代碼只能在特定域名下運行,這樣就可以降低被模擬的風險。

示例如下:

const code = `
console.log('hello world')
`

const options = {
  domainLock: ['cuiqingcai.com']
}

運行結(jié)果如下:

var _0x3203=['apply','return\x20(function()\x20','{}.constructor(\x22return\x20this\x22)(\x20)','item','attribute','value','replace','length','charCodeAt','log','hello\x20world'];(function(_0x2ed22c,_0x3ad370){var _0x49dc54=function(_0x53a786){while(--_0x53a786){_0x2ed22c['push'](_0x2ed22c['shift']());}};_0x49dc54(++_0x3ad370);}(_0x3203,0x155));var _0x5b38=function(_0xd7780b,_0x19c0f2){_0xd7780b=_0xd7780b-0x0;var _0x2d2f44=_0x3203[_0xd7780b];return _0x2d2f44;};var _0x485919=function(){var _0x5cf798=!![];return function(_0xd1fa29,_0x2ed646){var _0x56abf=_0x5cf798?function(){if(_0x2ed646){var _0x33af63=_0x2ed646[_0x5b38('0x0')](_0xd1fa29,arguments);_0x2ed646=null;return _0x33af63;}}:function(){};_0x5cf798=![];return _0x56abf;};}();var _0x67dcc8=_0x485919(this,function(){var _0x276a31;try{var _0x5c8be2=Function(_0x5b38('0x1')+_0x5b38('0x2')+');');_0x276a31=_0x5c8be2();}catch(_0x5f1c00){_0x276a31=window;}var _0x254a0d=function(){return{'key':_0x5b38('0x3'),'value':_0x5b38('0x4'),'getAttribute':function(){for(var _0x5cc3c7=0x0;_0x5cc3c7<0x3e8;_0x5cc3c7--){var _0x35b30b=_0x5cc3c7>0x0;switch(_0x35b30b){case!![]:return this[_0x5b38('0x3')]+'_'+this[_0x5b38('0x5')]+'_'+_0x5cc3c7;default:this[_0x5b38('0x3')]+'_'+this[_0x5b38('0x5')];}}}()};};var _0x3b375a=new RegExp('[QLCIKYkCFzdWpzRAXMhxJOYpTpYWJHPll]','g');var _0x5a94d2='cuQLiqiCInKYkgCFzdWcpzRAaXMi.hcoxmJOYpTpYWJHPll'[_0x5b38('0x6')](_0x3b375a,'')['split'](';');var _0x5c0da2;var _0x19ad5d;var _0x5992ca;var _0x40bd39;for(var _0x5cad1 in _0x276a31){if(_0x5cad1[_0x5b38('0x7')]==0x8&&_0x5cad1[_0x5b38('0x8')](0x7)==0x74&&_0x5cad1[_0x5b38('0x8')](0x5)==0x65&&_0x5cad1[_0x5b38('0x8')](0x3)==0x75&&_0x5cad1[_0x5b38('0x8')](0x0)==0x64){_0x5c0da2=_0x5cad1;break;}}for(var _0x29551 in _0x276a31[_0x5c0da2]){if(_0x29551[_0x5b38('0x7')]==0x6&&_0x29551[_0x5b38('0x8')](0x5)==0x6e&&_0x29551[_0x5b38('0x8')](0x0)==0x64){_0x19ad5d=_0x29551;break;}}if(!('~'>_0x19ad5d)){for(var _0x2b71bd in _0x276a31[_0x5c0da2]){if(_0x2b71bd[_0x5b38('0x7')]==0x8&&_0x2b71bd[_0x5b38('0x8')](0x7)==0x6e&&_0x2b71bd[_0x5b38('0x8')](0x0)==0x6c){_0x5992ca=_0x2b71bd;break;}}for(var _0x397f55 in _0x276a31[_0x5c0da2][_0x5992ca]){if(_0x397f55['length']==0x8&&_0x397f55[_0x5b38('0x8')](0x7)==0x65&&_0x397f55[_0x5b38('0x8')](0x0)==0x68){_0x40bd39=_0x397f55;break;}}}if(!_0x5c0da2||!_0x276a31[_0x5c0da2]){return;}var _0x5f19be=_0x276a31[_0x5c0da2][_0x19ad5d];var _0x674f76=!!_0x276a31[_0x5c0da2][_0x5992ca]&&_0x276a31[_0x5c0da2][_0x5992ca][_0x40bd39];var _0x5e1b34=_0x5f19be||_0x674f76;if(!_0x5e1b34){return;}var _0x593394=![];for(var _0x479239=0x0;_0x479239<_0x5a94d2['length'];_0x479239++){var _0x19ad5d=_0x5a94d2[_0x479239];var _0x112c24=_0x5e1b34['length']-_0x19ad5d['length'];var _0x51731c=_0x5e1b34['indexOf'](_0x19ad5d,_0x112c24);var _0x173191=_0x51731c!==-0x1&&_0x51731c===_0x112c24;if(_0x173191){if(_0x5e1b34['length']==_0x19ad5d[_0x5b38('0x7')]||_0x19ad5d['indexOf']('.')===0x0){_0x593394=!![];}}}if(!_0x593394){data;}else{return;}_0x254a0d();});_0x67dcc8();console[_0x5b38('0x9')](_0x5b38('0xa'));

這段代碼就只能在指定域名 cuiqingcai.com 下運行,不能在其他網(wǎng)站運行,不信你試試。

特殊編碼

另外還有一些特殊的工具包,如使用 aaencode、jjencode、jsfuck 等工具對代碼進行混淆和編碼。

示例如下:

var a = 1

jsfuck 的結(jié)果:

[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[+[]]+([][[]]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![])([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![]+!![])(([]+{})[+[]])[+[]]+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+[]))+(+{}+[])[+!![]]+(!![]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[+[]]+([][[]]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![])([][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][([]+{})[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+[]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+([]+{})[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!![]]+([][[]]+[])[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+([]+{})[+!![]]+([]+{})[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][[]]+[])[!+[]+!![]+!![]+!![]+!![]]+([]+{})[+!![]]+([][[]]+[])[+!![]])(!+[]+!![]+!![]+!![]+!![]))[!+[]+!![]+!![]]+([][[]]+[])[!+[]+!![]+!![]])(!+[]+!![]+!![]+!![]+!![])(([]+{})[+[]])[+[]]+(!+[]+!![]+!![]+[])+([][[]]+[])[!+[]+!![]])+([]+{})[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(+!![]+[]))(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])

aaencode 的結(jié)果:

?ω??= /`m′)? ~┻━┻ / ['_']; o=(???) =_=3; c=(?Θ?) =(???)-(???); (?Д?) =(?Θ?)= (o^_^o)/ (o^_^o);(?Д?)={?Θ?: '_' ,?ω?? : ((?ω??==3) +'_') [?Θ?] ,???? :(?ω??+ '_')[o^_^o -(?Θ?)] ,?Д??:((???==3) +'_')[???] }; (?Д?) [?Θ?] =((?ω??==3) +'_') [c^_^o];(?Д?) ['c'] = ((?Д?)+'_') [ (???)+(???)-(?Θ?) ];(?Д?) ['o'] = ((?Д?)+'_') [?Θ?];(?o?)=(?Д?) ['c']+(?Д?) ['o']+(?ω?? +'_')[?Θ?]+ ((?ω??==3) +'_') [???] + ((?Д?) +'_') [(???)+(???)]+ ((???==3) +'_') [?Θ?]+((???==3) +'_') [(???) - (?Θ?)]+(?Д?) ['c']+((?Д?)+'_') [(???)+(???)]+ (?Д?) ['o']+((???==3) +'_') [?Θ?];(?Д?) ['_'] =(o^_^o) [?o?] [?o?];(?ε?)=((???==3) +'_') [?Θ?]+ (?Д?) .?Д??+((?Д?)+'_') [(???) + (???)]+((???==3) +'_') [o^_^o -?Θ?]+((???==3) +'_') [?Θ?]+ (?ω?? +'_') [?Θ?]; (???)+=(?Θ?); (?Д?)[?ε?]='\\'; (?Д?).?Θ??=(?Д?+ ???)[o^_^o -(?Θ?)];(o???o)=(?ω?? +'_')[c^_^o];(?Д?) [?o?]='\'';(?Д?) ['_'] ( (?Д?) ['_'] (?ε?+(?Д?)[?o?]+ (?Д?)[?ε?]+(?Θ?)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (?Д?)[?ε?]+(?Θ?)+ (???)+ (?Θ?)+ (?Д?)[?ε?]+(?Θ?)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (?Θ?))+ (?Д?)[?ε?]+(???)+ (c^_^o)+ (?Д?)[?ε?]+(?Θ?)+ (???)+ (?Θ?)+ (?Д?)[?ε?]+(???)+ (c^_^o)+ (?Д?)[?ε?]+((???) + (o^_^o))+ ((???) + (?Θ?))+ (?Д?)[?ε?]+(???)+ (c^_^o)+ (?Д?)[?ε?]+((o^_^o) +(o^_^o))+ (?Θ?)+ (?Д?)[?o?])(?Θ?))((?Θ?)+(?Д?)[?ε?]+((???)+(?Θ?))+(?Θ?)+(?Д?)[?o?]);

jjencode 的結(jié)果:

$=~[];$={___:++$,$$$$:(![]+'')[$],__$:++$,$_$_:(![]+'')[$],_$_:++$,$_$$:({}+'')[$],$$_$:($[$]+'')[$],_$$:++$,$$$_:(!''+'')[$],$__:++$,$_$:++$,$$__:({}+'')[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+'')[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+'')[$.__$])+((!$)+'')[$._$$]+($.__=$.$_[$.$$_])+($.$=(!''+'')[$.__$])+($._=(!''+'')[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!''+'')[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+'\''+'\\'+$.__$+$.$$_+$.$$_+$.$_$_+'\\'+$.__$+$.$$_+$._$_+'\\'+$.$__+$.___+$.$_$_+'\\'+$.$__+$.___+'=\\'+$.$__+$.___+$.__$+'\'')())();

這些混淆方式比較另類,但只需要輸入到控制臺即可執(zhí)行,其沒有真正達到強力混淆的效果。

以上便是對 JavaScript 混淆方式的介紹和總結(jié)。總的來說,經(jīng)過混淆的 JavaScript 代碼其可讀性大大降低,同時防護效果也大大增強。

JavaScript 加密

不同于 JavaScript 混淆技術,JavaScript 加密技術可以說是對 JavaScript 混淆技術防護的進一步升級,其基本思路是將一些核心邏輯使用諸如 C/C++ 語言來編寫,并通過 JavaScript 調(diào)用執(zhí)行,從而起到二進制級別的防護作用。

其加密的方式現(xiàn)在有 Emscripten 和 WebAssembly 等,其中后者越來越成為主流。

下面我們分別來介紹下。

Emscripten

現(xiàn)在,許多 3D 游戲都是用 C/C++ 語言寫的,如果能將 C / C++ 語言編譯成 JavaScript 代碼,它們不就能在瀏覽器里運行了嗎?眾所周知,JavaScript 的基本語法與 C 語言高度相似。于是,有人開始研究怎么才能實現(xiàn)這個目標,為此專門做了一個編譯器項目 Emscripten。這個編譯器可以將 C / C++ 代碼編譯成 JavaScript 代碼,但不是普通的 JavaScript,而是一種叫做 asm.js 的 JavaScript 變體。

因此說,某些 JavaScript 的核心功能可以使用 C/C++ 語言實現(xiàn),然后通過 Emscripten 編譯成 asm.js,再由 JavaScript 調(diào)用執(zhí)行,這可以算是一種前端加密技術。

WebAssembly

如果你對 JavaScript 比較了解,可能知道還有一種叫做 WebAssembly 的技術,也能將 C/C++ 轉(zhuǎn)成 JavaScript 引擎可以運行的代碼。那么它與 asm.js 有何區(qū)別呢?

其實兩者的功能基本一致,就是轉(zhuǎn)出來的代碼不一樣:asm.js 是文本,WebAssembly 是二進制字節(jié)碼,因此運行速度更快、體積更小。從長遠來看,WebAssembly 的前景更光明。

WebAssembly 是經(jīng)過編譯器編譯之后的字節(jié)碼,可以從 C/C++ 編譯而來,得到的字節(jié)碼具有和 JavaScript 相同的功能,但它體積更小,而且在語法上完全脫離 JavaScript,同時具有沙盒化的執(zhí)行環(huán)境。

利用 WebAssembly 技術,我們可以將一些核心的功能利用 C/C++ 語言實現(xiàn),形成瀏覽器字節(jié)碼的形式。然后在 JavaScript 中通過類似如下的方式調(diào)用:

WebAssembly.compile(new Uint8Array(`
  00 61 73 6d  01 00 00 00  01 0c 02 60  02 7f 7f 01
  7f 60 01 7f  01 7f 03 03  02 00 01 07  10 02 03 61
  64 64 00 00  06 73 71 75  61 72 65 00  01 0a 13 02
  08 00 20 00  20 01 6a 0f  0b 08 00 20  00 20 00 6c
  0f 0b`
.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(module => {
  const instance = new WebAssembly.Instance(module)
  const { add, square } = instance.exports
  console.log('2 + 4 =', add(24))
  console.log('3^2 =', square(3))
  console.log('(2 + 5)^2 =', square(add(2 + 5)))
})

這種加密方式更加安全,因為作為二進制編碼它能起到的防護效果無疑是更好的。如果想要逆向或破解那得需要逆向 WebAssembly,難度也是很大的。

總結(jié)

以上,我們就介紹了接口加密技術和 JavaScript 的壓縮、混淆和加密技術,知己知彼方能百戰(zhàn)不殆,了解了原理,我們才能更好地去實現(xiàn) JavaScript 的逆向。

代碼:https://github.com/Python3WebSpider/JavaScriptObfuscate

參考文獻

  • https://www./blog/2017/09/asmjs_emscripten.html
  • https:///post/5cfcb9d25188257e853fa71c#heading-23
  • https://www.jianshu.com/p/326594cbd4fa
  • https://github.com/javascript-obfuscator/javascript-obfuscator
  • https:///
  • https://www./jjencode.html
  • http://dean./packer/



崔慶才

靜覓博客博主,《Python3網(wǎng)絡爬蟲開發(fā)實戰(zhàn)》作者

隱形字

個人公眾號:進擊的Coder

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多